<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Giovanni Fontana</title>
    <description>Specialist Solution Architect at Red Hat | Red Hat Certified Architect (RHCA) | Author of “OpenShift Multi-Cluster Management Handbook”</description>
    <link>https://giofontana.github.io/</link>
    <atom:link href="https://giofontana.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 16 Mar 2026 21:08:49 +0000</pubDate>
    <lastBuildDate>Mon, 16 Mar 2026 21:08:49 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>

    
      <item>
        <title>Cert expired again! How I quickly renewed my OpenShift cluster&apos;s certificate and automated renewals with Claude Code!</title>
        <description>&lt;h2 id=&quot;ruining-my-first-coffee&quot;&gt;Ruining My First Coffee!&lt;/h2&gt;

&lt;p&gt;Nothing ruins a peaceful morning quite like a certificate expiration. There I was, coffee in hand, when I noticed certificate alerts for many of the apps running in my lab. My thoughts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“Ouch, I forgot to renew it again!”&lt;/li&gt;
  &lt;li&gt;“I have an important meeting tomorrow, I may need this environment running properly”&lt;/li&gt;
  &lt;li&gt;“I will have meetings until 11am, can’t do it before then…”&lt;/li&gt;
  &lt;li&gt;“Wait, I do have Claude Code now… what if I ask it to do it for me?!”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how it all went down!&lt;/p&gt;

&lt;p&gt;For the past year, I’d been managing this certificate manually through a tedious process:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;SSH into a remote server to request new certificates from Let’s Encrypt&lt;/li&gt;
  &lt;li&gt;Download the certificates and encrypt them with Sealed Secrets&lt;/li&gt;
  &lt;li&gt;Create a new SealedSecret manifest&lt;/li&gt;
  &lt;li&gt;Commit to Git and wait for Argo CD to sync&lt;/li&gt;
  &lt;li&gt;Hope everything works without breaking ingress&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every. Single. 90 days.&lt;/p&gt;

&lt;p&gt;I wanted to automate this process with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cert-manager&lt;/code&gt;, but I’d never had the time to create the necessary GitOps configuration files for deployment and setup (I try to deploy and manage everything in my lab using GitOps wherever possible, so I can quickly re-build it from scratch if I have to).&lt;/p&gt;

&lt;p&gt;So, this was the last time. I decided this morning would be different. I’d use Claude Code to not only handle this renewal quickly but finally implement the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cert-manager&lt;/code&gt; with GitOps that I wanted to do long ago.&lt;/p&gt;

&lt;h2 id=&quot;act-1-the-quick-fix-morning-emergency&quot;&gt;Act 1: The Quick Fix (Morning Emergency)&lt;/h2&gt;

&lt;p&gt;First things first—I needed to renew the expiring certificate before thinking about automation. With Claude Code as my AI pair programming partner, what usually took me an hour was done in minutes.&lt;/p&gt;

&lt;p&gt;Claude Code helped me:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Navigate to the remote server and run the certificate renewal commands&lt;/li&gt;
  &lt;li&gt;Properly encrypt the new certificate with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeseal&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Update the SealedSecret manifest in my GitOps repository&lt;/li&gt;
  &lt;li&gt;Verify the deployment through Argo CD&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Time saved:&lt;/strong&gt; What normally took me 20-30 minutes of manual steps was reduced to no more than 10 minutes (well, honestly it took basically 1 or 2 minutes of my time, the rest was observing Claude to do it and approving execution of codes and commands).&lt;/p&gt;

&lt;p&gt;But as I watched Argo CD sync the new certificate, I knew this was just postponing the inevitable. In 90 days, I’d be back here doing the same thing. It was time to automate.&lt;/p&gt;

&lt;h2 id=&quot;act-2-the-automation-decision&quot;&gt;Act 2: The Automation Decision&lt;/h2&gt;

&lt;p&gt;Over my second coffee, I outlined what I needed:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Automatic certificate renewal&lt;/strong&gt;: Use cert-manager to automatically renew OpenShift Default Ingress certificate.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GitOps-native&lt;/strong&gt;: Everything managed declaratively through Argo CD.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Let’s Encrypt integration&lt;/strong&gt;: Continue using free, trusted certificates.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;DNS-01 challenge&lt;/strong&gt;: For wildcard certificates with Cloudflare DNS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I told Claude Code: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Now, can you read the repo at &amp;lt;folder for my gitops manifests&amp;gt; and suggest changes to be able to use cert-manager to auto renew default ingress cert? cert-manager operator needs to be installed and configure through GitOps (Argo CD)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;What followed was an impressive demonstration of how helpful Claude can be to automate repetitive tasks very quickly.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2026-03-16-automating-openshift-certificates-with-claude-code/01.png&quot; alt=&quot;The first prompt&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;act-3-the-implementation-journey&quot;&gt;Act 3: The Implementation Journey&lt;/h2&gt;

&lt;h3 id=&quot;understanding-the-environment&quot;&gt;Understanding the Environment&lt;/h3&gt;

&lt;p&gt;Claude Code immediately understood my GitOps repository structure:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href=&quot;https://github.com/giofontana/gitops-ocp-infra&quot;&gt;gitops-ocp-infra&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Pattern:&lt;/strong&gt; Cluster-specific configurations with shared operator bases&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Target:&lt;/strong&gt; simpsons cluster at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.apps.simpsons.lab.gfontana.me&lt;/code&gt; (Curious why this cluster is named “Simpsons”? Check the reason in the end of this article).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GitOps Engine:&lt;/strong&gt; Argo CD managing all cluster resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;phase-1-leveraging-my-current-gitops-standards&quot;&gt;Phase 1: Leveraging my current GitOps standards&lt;/h3&gt;

&lt;p&gt;Instead of creating operator manifests from scratch, Claude Code recognized the pattern I use for GitOps and suggested the implementation of cert-manager following the same pattern.&lt;/p&gt;

&lt;p&gt;My GitOps follows a layered based approach, in which I separate shared operators and manifests from cluster specific configurations. I also use an Aggregation layer and App-of-Apps to bootstrap all apps. It is not a simple framework, it took me a very long time to develop it and Claude was able to understand it in a matter of seconds. I am planning to publish an article specifically about the way I structure and use GitOps, stay tuned!&lt;/p&gt;

&lt;p&gt;One thing that Claude wasn’t able to detect initially was how I re-use manifests from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gitops-catalog&lt;/code&gt;. Initially it created all manifests to deploy cert-manager operator (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OperatorGroup&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subscription&lt;/code&gt;, etc.). I had to ask Claude the following:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2026-03-16-automating-openshift-certificates-with-claude-code/02.png&quot; alt=&quot;A quick addition&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note: This folder points to &lt;a href=&quot;https://github.com/giofontana/gitops-catalog&quot;&gt;gitops-catalog&lt;/a&gt;, which is a fork of &lt;a href=&quot;https://github.com/redhat-cop/gitops-catalog&quot;&gt;this repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Claude understood what I wanted and updated the cert-manager operator kustomization YAML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href=&quot;https://github.com/giofontana/gitops-ocp-infra/blob/main/gitops/manifests/operators/cert-manager-operator/overlays/stable/kustomization.yaml&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/gitops/manifests/operators/cert-manager-operator/overlays/stable/kustomization.yaml&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kustomize.config.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Kustomization&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://github.com/giofontana/gitops-catalog//openshift-cert-manager-operator/operator/overlays/stable-v1?ref=main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This approach:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Reused well-tested configurations&lt;/li&gt;
  &lt;li&gt;Maintained a single source of truth&lt;/li&gt;
  &lt;li&gt;Eliminated duplicate manifests&lt;/li&gt;
  &lt;li&gt;Referenced the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stable-v1&lt;/code&gt; channel automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;phase-2-cloudflare-dns-01-configuration&quot;&gt;Phase 2: Cloudflare DNS-01 Configuration&lt;/h3&gt;

&lt;p&gt;For wildcard certificates, DNS-01 challenges are required. Claude Code helped me configure the ClusterIssuer with Cloudflare integration:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href=&quot;https://github.com/giofontana/gitops-ocp-infra/blob/main//gitops/manifests/clusters/simpsons/configuration/cert-manager/clusterissuer-letsencrypt-prod.yaml&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/gitops/manifests/clusters/simpsons/configuration/cert-manager/clusterissuer-letsencrypt-prod.yaml&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cert-manager.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ClusterIssuer&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;letsencrypt-prod&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;acme&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://acme-v02.api.letsencrypt.org/directory&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;admin@gfontana.me&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;privateKeySecretRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;letsencrypt-prod-account-key&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;solvers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;dns01&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;cloudflare&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;admin@gfontana.me&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;apiTokenSecretRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cloudflare-api-token&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;api-token&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;dnsZones&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gfontana.me&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Security note:&lt;/strong&gt; The Cloudflare API token was stored as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SealedSecret&lt;/code&gt;, encrypted and safe for Git storage.&lt;/p&gt;

&lt;h3 id=&quot;phase-3-certificate-resource-definition&quot;&gt;Phase 3: Certificate Resource Definition&lt;/h3&gt;

&lt;p&gt;Here’s where things got interesting. Claude Code initially created the Certificate resource. We deployed it, watched the logs, and… &lt;strong&gt;error&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-commonname-bug-fix&quot;&gt;The commonName Bug Fix&lt;/h3&gt;

&lt;p&gt;I messed it up! Claude correctly set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commonName&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dnsNames&lt;/code&gt;. However, I changed it to add some other aliases and mistakenly removed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gfontana.me&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dnsNames&lt;/code&gt;, which caused the certificate request to fail with a validation error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href=&quot;https://github.com/giofontana/gitops-ocp-infra/blob/main/gitops/manifests/clusters/simpsons/configuration/cert-manager-certs/certificate-wildcard-ingress.yaml&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gitops/manifests/clusters/simpsons/configuration/cert-manager-certs/certificate-wildcard-ingress.yaml&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cert-manager.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Certificate&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wildcard-apps-cert&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;openshift-ingress&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;secretName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wildcard-apps-tls&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;issuerRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;letsencrypt-prod&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ClusterIssuer&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;commonName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gfontana.me&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;dnsNames&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gfontana.me&quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# &amp;lt;-- This line was missing, causing the error&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*.gfontana.me&quot;&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*.simpsons.lab.gfontana.me&quot;&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*.apps.simpsons.lab.gfontana.me&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;usages&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;digital signature&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;key encipherment&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;server auth&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;renewBefore&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;720h&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Renew 30 days before expiry&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Claude Code not only identified the failure, but also fixed it and asked my permission:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2026-03-16-automating-openshift-certificates-with-claude-code/03.png&quot; alt=&quot;Claude troubleshooting&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Within minutes of the fix, the certificate was issued successfully by Let’s Encrypt.&lt;/p&gt;

&lt;h3 id=&quot;phase-4-ingresscontroller-integration&quot;&gt;Phase 4: IngressController Integration&lt;/h3&gt;

&lt;p&gt;The final piece was updating OpenShift’s IngressController to use the new certificate. Claude Code created an Argo CD PostSync hook job:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;batch/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Job&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;patch-ingress-cert-manager&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;openshift-ingress-operator&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;argocd.argoproj.io/hook&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;PostSync&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;argocd.argoproj.io/hook-delete-policy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;BeforeHookCreation&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;patch&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;registry.redhat.io/openshift4/ose-cli:latest&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/bin/bash&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;-c&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;oc patch ingresscontroller default \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;-n openshift-ingress-operator \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;--type=merge \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;-p &apos;{&quot;spec&quot;:{&quot;defaultCertificate&quot;:{&quot;name&quot;:&quot;wildcard-apps-tls&quot;}}}&apos;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;restartPolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Never&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;serviceAccountName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cli-job-sa&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;backoffLimit&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This job runs automatically after Argo CD syncs, ensuring the IngressController always uses the cert-manager-issued certificate.&lt;/p&gt;

&lt;h3 id=&quot;phase-5-argo-cd-application-definitions&quot;&gt;Phase 5: Argo CD Application Definitions&lt;/h3&gt;

&lt;p&gt;Finally, Claude Code created the Argo CD Application resources to manage everything. My initial thinking was to have only a single Argo CD Application for it. However, Claude created two, the first for the operator itself and second for ingress certificate - which makes a lot of sense and even better than I thought initially!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;cert-manager Application (Wave 1):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;argoproj.io/v1alpha1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Application&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cert-manager&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;openshift-gitops&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;argocd.argoproj.io/sync-wave&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;1&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;openshift-gitops&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://kubernetes.default.svc&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gitops/manifests/clusters/simpsons/aggregate/infra/cert-manager/&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;repoURL&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://github.com/giofontana/gitops-ocp-infra.git&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;targetRevision&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;main&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;syncPolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;automated&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;prune&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;selfHeal&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;cert-manager-certs Application (Wave 3):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;argoproj.io/v1alpha1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Application&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cert-manager-certs&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;openshift-gitops&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;argocd.argoproj.io/sync-wave&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;openshift-gitops&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://kubernetes.default.svc&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gitops/manifests/clusters/simpsons/aggregate/infra/cert-manager-certs/&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;repoURL&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://github.com/giofontana/gitops-ocp-infra.git&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;targetRevision&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;main&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;syncPolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;automated&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;prune&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;selfHeal&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Sync waves&lt;/strong&gt; ensure proper ordering: operator and issuer first (wave 1), then certificates and ingress configuration (wave 3).&lt;/p&gt;

&lt;h2 id=&quot;act-4-its-magic&quot;&gt;Act 4: “It’s Magic!”&lt;/h2&gt;

&lt;p&gt;After pushing all changes to Git, I watched Argo CD work its magic:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;oc get certificate wildcard-apps-cert &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-ingress
NAME                 READY   SECRET              AGE
wildcard-apps-cert   True    wildcard-apps-tls   5m
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;True&lt;/code&gt; means the magic worked! Let me verify:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;oc get secret wildcard-apps-tls &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-ingress &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;{.data.tls\.crt}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;base64&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; | openssl x509 &lt;span class=&quot;nt&quot;&gt;-noout&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-dates&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-issuer&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;notBefore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Mar 16 12:00:00 2026 GMT
&lt;span class=&quot;nv&quot;&gt;notAfter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;Jun 14 12:00:00 2026 GMT
&lt;span class=&quot;nv&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;C &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; US, O &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Let&lt;span class=&quot;s1&quot;&gt;&apos;s Encrypt, CN = R12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Perfect.&lt;/strong&gt; A legitimate Let’s Encrypt certificate, valid for 90 days, with automatic renewal scheduled for May 15, 2026 (30 days before expiry). The OpenShift console was now serving the Let’s Encrypt certificate. The router pods had restarted automatically, and everything just worked.&lt;/p&gt;

&lt;h2 id=&quot;what-made-claude-code-special&quot;&gt;What Made Claude Code Special&lt;/h2&gt;

&lt;p&gt;This wasn’t just about saving time—it was about the quality of the collaboration. Here’s what impressed me:&lt;/p&gt;

&lt;h3 id=&quot;1-pattern-recognition&quot;&gt;1. Pattern Recognition&lt;/h3&gt;
&lt;p&gt;Claude Code immediately understood my GitOps repository structure and created all the new manifests following it. This kind of contextual awareness is exactly what you want in a pair programming partner.&lt;/p&gt;

&lt;h3 id=&quot;2-real-time-debugging&quot;&gt;2. Real-Time Debugging&lt;/h3&gt;
&lt;p&gt;When the certificate request failed due to the missing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gfontana.me&lt;/code&gt; entry in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dnsNames&lt;/code&gt;, Claude Code:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Analyzed the error logs&lt;/li&gt;
  &lt;li&gt;Identified the root cause&lt;/li&gt;
  &lt;li&gt;Proposed the fix&lt;/li&gt;
  &lt;li&gt;Implemented it immediately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No googling, or searching Stack Overflow. No reading through cert-manager documentation for 30 minutes.&lt;/p&gt;

&lt;h3 id=&quot;3-best-practices-built-in&quot;&gt;3. Best Practices Built-In&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Sync waves for proper resource ordering&lt;/li&gt;
  &lt;li&gt;SealedSecrets for sensitive data&lt;/li&gt;
  &lt;li&gt;PostSync hooks for IngressController patching&lt;/li&gt;
  &lt;li&gt;Proper RBAC for the patch job&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Claude Code knew these patterns and applied them without being told.&lt;/p&gt;

&lt;h3 id=&quot;4-multi-layer-understanding&quot;&gt;4. Multi-Layer Understanding&lt;/h3&gt;
&lt;p&gt;The implementation required knowledge of:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;OpenShift IngressController architecture&lt;/li&gt;
  &lt;li&gt;cert-manager CRDs and workflows&lt;/li&gt;
  &lt;li&gt;Kustomize overlays and aggregation&lt;/li&gt;
  &lt;li&gt;Argo CD application patterns&lt;/li&gt;
  &lt;li&gt;Kubernetes RBAC and ServiceAccounts&lt;/li&gt;
  &lt;li&gt;Let’s Encrypt ACME protocol and DNS-01 challenges&lt;/li&gt;
  &lt;li&gt;Cloudflare API integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Claude Code navigated all these layers seamlessly and very quickly.&lt;/p&gt;

&lt;h3 id=&quot;5-adaptive-problem-solving&quot;&gt;5. Adaptive Problem-Solving&lt;/h3&gt;
&lt;p&gt;When initial approaches didn’t work, Claude Code didn’t retry the same commands. It analyzed, adapted, and found alternative solutions. That’s intelligent automation.&lt;/p&gt;

&lt;h2 id=&quot;the-icing-on-the-cake---this-article&quot;&gt;The icing on the cake - This article!&lt;/h2&gt;

&lt;p&gt;To end my engagement with Claude in the best way I asked it the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Now use all the procedure executed here and write an article about how I used Claude Code this morning to quickly renew OpenShift certificates for my lab and also deployed cert-manager and configure it with Argo CD to auto renew the certificates. Write an article in my github pages at: &amp;lt;my github pages&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It learned my GitHub pages structure and wrote a big part of this article for me. I just had to review it, add my personal touch and style, and voilà—a new blog post ready to publish!&lt;/p&gt;

&lt;h2 id=&quot;conclusion-a-morning-well-spent&quot;&gt;Conclusion: A Morning Well Spent&lt;/h2&gt;

&lt;p&gt;What started as a frustrating certificate expiration alert turned into a nice experimentation experience with Claude Code. With Claude Code as my AI pair programming partner, I:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Quickly renewed&lt;/strong&gt; an expiring certificate in minutes instead of hours&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Deployed cert-manager&lt;/strong&gt; using GitOps best practices&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Automated renewals&lt;/strong&gt;: certificates now auto-renew 30 days before expiration&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Eliminated toil&lt;/strong&gt; and saved a few hours annually&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Learned patterns&lt;/strong&gt; I can apply to other infrastructure automation&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Shared my experience&lt;/strong&gt; by writing this article&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The real revelation wasn’t just the time saved—it was the quality of the collaboration. Claude Code understood my environment, adapted to my patterns, debugged issues in real-time, and applied best practices throughout.&lt;/p&gt;

&lt;p&gt;What about you? Are you using AI assistants like Claude Code to automate repetitive tasks? Share your thoughts! I’d love to hear your experiences!&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href=&quot;https://github.com/giofontana/gitops-ocp-infra&quot;&gt;gitops-ocp-infra&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;cert-manager Documentation:&lt;/strong&gt; &lt;a href=&quot;https://cert-manager.io/&quot;&gt;cert-manager.io&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Claude Code:&lt;/strong&gt; &lt;a href=&quot;https://www.anthropic.com/&quot;&gt;Anthropic’s AI-Powered CLI&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Argo CD:&lt;/strong&gt; &lt;a href=&quot;https://argoproj.github.io/cd/&quot;&gt;argoproj.github.io/cd&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;why-simpsons&quot;&gt;Why “Simpsons”?&lt;/h2&gt;

&lt;p&gt;A couple of years back I struggled a lot to find good names for my lab resources and easily remember them. So I decided to name all my lab resources after cartoons. I love Simpsons, so decided to name my main cluster as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simpsons&lt;/code&gt;. This cluster has 4 nodes: Homer, Marge, Bart and Lisa. The node with more resources - biggest or fatter - is obviously Homer. The smartest (which has a GPU) is Lisa. And so on. I have another small cluster that sits next to Simpsons, what is its name? Flanders, of course!&lt;/p&gt;

&lt;p&gt;So, that way I gave names that are meaningful and help me remember what they have… and it is also kind of funny! lol&lt;/p&gt;
</description>
        <pubDate>Mon, 16 Mar 2026 01:00:00 +0000</pubDate>
        <link>https://giofontana.github.io/blog/2026-03-16-automating-openshift-certificates-with-claude-code/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/2026-03-16-automating-openshift-certificates-with-claude-code/</guid>
      </item>
    
      <item>
        <title>The Cost of Generative AI</title>
        <description>&lt;h1 id=&quot;understanding-the-true-cost-of-ai-systems-lessons-from-mit-professional-education&quot;&gt;Understanding the True Cost of AI Systems: Lessons from MIT Professional Education&lt;/h1&gt;

&lt;p&gt;Recently, I started the course &lt;a href=&quot;https://professional.mit.edu/course-catalog/applied-agentic-ai-organizational-transformation&quot;&gt;&lt;strong&gt;“Applied Agentic AI for Organizational Transformation”&lt;/strong&gt;&lt;/a&gt; with &lt;strong&gt;MIT Professional Education&lt;/strong&gt; to learn more about Agentic AI and all the buzzwords surrounding AI these days. To my surprise, the first module is not about Agentic AI itself, but about the cost of AI Systems. My first reaction was, “Wait, what?! Why start with costs?!”&lt;/p&gt;

&lt;p&gt;As it turns out, it was quite eye-opening to learn how complex the cost structure of Large Language Models (LLMs) truly is. There are numerous components you must consider to keep your budget under control. This article is inspired by the first assignment of this course, featuring a hypothetical AI assistant called &lt;strong&gt;DIYgpt&lt;/strong&gt;. I hope this helps shed light on what you need to consider for your own applications.&lt;/p&gt;

&lt;h1 id=&quot;the-use-case-diygpt&quot;&gt;The Use Case: DIYgpt&lt;/h1&gt;

&lt;p&gt;To evaluate costs, I envisioned &lt;strong&gt;DIYgpt&lt;/strong&gt;, an AI assistant designed to guide DIY enthusiasts through home projects like carpentry and repairs. Users describe a task, and the AI generates a step-by-step tutorial with a parts list.&lt;/p&gt;

&lt;p&gt;To get more realistic data, I tested prompts across different models (like ChatGPT and Google Gemini) to find the average token counts for inputs and outputs.&lt;/p&gt;

&lt;h2 id=&quot;the-data-points&quot;&gt;The Data Points&lt;/h2&gt;

&lt;p&gt;The prompts below were utilized for testing and establishing the average token consumption. It should be noted that a real-world use case would typically involve a greater number of prompts, more iterations, and a wider variety of models.&lt;/p&gt;

&lt;p&gt;1) “I want to build a simple wooden bookshelf for my home office. It needs to be 4 feet tall and 3 feet wide with three shelves. What materials do I need to buy at the hardware store, and what are the step-by-step assembly instructions?”&lt;/p&gt;

&lt;p&gt;2) “My kitchen faucet is leaking from the base of the handle whenever I turn the water on. I have a standard hex wrench set and a screwdriver. Can you walk me through how to diagnose and fix this?”&lt;/p&gt;

&lt;p&gt;3) “I just bought my first power drill. I see a bunch of numbers on a dial near the tip (1 through 20). What those numbers mean, and which one should I use if I’m just driving a screw into a piece of pine?”&lt;/p&gt;

&lt;p&gt;The result is shown below.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Model&lt;/strong&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Question&lt;/strong&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Prompt (tokens)&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;Response (tokens)&lt;/strong&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;ChatGPT&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;#1&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;52&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;834&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;52&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;828&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;52&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;711&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Google Gemini Fast&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;52&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;620&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;52&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;643&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;52&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;704&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;ChatGPT&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;#2&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;41&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;630&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;41&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;541&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;41&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;516&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Google Gemini Fast&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;41&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;622&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;41&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;675&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;41&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;628&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;ChatGPT&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;#3&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;50&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;402&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;50&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;288&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;50&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;272&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Google Gemini Fast&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;50&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;381&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;50&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;465&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;50&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;434&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Average&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;47&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;566&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Therefore, the following average token counts was used in this execise:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Input: 47 tokens (average of all inputs).&lt;/li&gt;
  &lt;li&gt;Output: 566 tokens (average of all outputs).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;projecting-the-burn-daily-and-monthly-costs&quot;&gt;&lt;strong&gt;Projecting the “Burn”: Daily and Monthly Costs&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;To estimate usage of the DIYgpt, the following assumptions were made:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Daily Active Users (DAU): 5,000&lt;/li&gt;
  &lt;li&gt;Sessions per User: 2&lt;/li&gt;
  &lt;li&gt;Queries per Session: 3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total: 5,000 users * 2 sessions * 3 queries = &lt;strong&gt;30,000 API calls per day&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Additionally, an average of &lt;strong&gt;10% of inputs were assumed to be cached&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Daily Token Volume&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Total volume of data being processed by the API:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Total Input Tokens: 30,000 calls * 47 tokens = &lt;strong&gt;1,410,000 tokens/day&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Standard Input Tokens (90%): &lt;strong&gt;1,269,000 tokens&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Cached Input Tokens (10%): &lt;strong&gt;141,000 tokens&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Total Output Tokens: 30,000 calls * 566 tokens = &lt;strong&gt;16,980,000 tokens/day&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Daily Cost Breakdown&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Applying the GPT-4.1 pricing model, as an example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Standard Input Cost: 1.269M tokens * $3.00 = &lt;strong&gt;$3.81&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Cached Input Cost: 0.141M tokens* 0.75 = &lt;strong&gt;$0.11&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Output Cost: 16.98M tokens * $12.00 = &lt;strong&gt;$203.76&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Total Daily Cost: &lt;strong&gt;$207.68&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Final Monthly Totals&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Assuming a standard 30-day billing cycle:&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Metric&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;Calculation&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;Result&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Daily Burn&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Sum of all token costs&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;$207.68&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Monthly Burn&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;$207.68 * 30 days&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;$6,230.40&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Cost Per Query&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;$207.68 / 30,000 calls&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;$0.0069&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Cost of the models:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Following the same line of reasoning for the remaining models, I compared several models. The difference in “daily burn” is staggering:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Model&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Daily Cost&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt; &lt;/th&gt;
      &lt;th&gt;Monthly Cost (30 Days)&lt;/th&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;Cost Per Query&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;o4-mini&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;$276.90&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;$8,306.91&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;$0.0092&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;GPT-4.1&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;$207.67&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td&gt;$6,230.18&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;$0.0069&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Gemini 3 Pro Preview&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;$206.33&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td&gt;$6,189.79&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;$0.0069&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;GPT-4.1 mini&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;$55.38&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td&gt;$1,661.38&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;$0.0018&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Gemini 3 Flash Preview&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;$51.58&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td&gt;$1,547.45&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;$0.0017&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;GPT-4.1 nano&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;$13.84&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt; &lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;$415.35&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;$0.0005&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2026-02-03-cost-of-ai/graph.png&quot; alt=&quot;Estimated Monthly Cost for DIYgpt&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The table and chart above illustrate the monthly “burn”. As you can see:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;o4-mini&lt;/strong&gt; sits at the top, representing the premium price for reasoning capabilities (approx. $8,306 per month). This is sort of expected, as o4-mini is a &lt;strong&gt;reasoning model&lt;/strong&gt; (the “o” series) which usually charges a premium for its “hidden” reasoning tokens - you are paying for the quality of the thought process, not just the final words.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GPT-4.1 nano&lt;/strong&gt; occupies the lowest bracket, demonstrating how architectural choices can reduce costs by over 95% compared to the most expensive model.The mid-range models (GPT-4.1 and Gemini 3 Pro) are nearly identical in cost, making their selection a matter of latency and accuracy rather than budget.&lt;/li&gt;
  &lt;li&gt;The &lt;strong&gt;mid-range&lt;/strong&gt; models (GPT-4.1 and Gemini 3 Pro) are &lt;strong&gt;nearly identical in cost&lt;/strong&gt;, making their selection a matter of latency and accuracy rather than budget.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The staggering difference in cost between these models is a great segue to what I want to discuss next.
&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;beyond-the-numbers-a-comprehensive-decision&quot;&gt;&lt;strong&gt;Beyond the Numbers: A Comprehensive Decision&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;While cost is a primary driver, choosing a model based solely on the “monthly burn” can be a mistake. To make a truly informed decision for an organization, you must consider additional factors, such as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Accuracy and Domain Expertise&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When evaluating AI systems, accuracy is paramount; a model with unacceptably low accuracy is essentially useless, regardless of its low cost. Therefore, you must define a strict minimum accuracy threshold and discard any model that fails to meet it. On the other hand, many applications do not require a costly, super-powerful reasoning model. The challenge lies in balancing performance, tailored to your specific domain, with cost. This is an ever-evolving field - recent developments include &lt;a href=&quot;https://llm-as-a-judge.github.io/&quot;&gt;“LLM-as-a-judge” concepts&lt;/a&gt;, but you can start with a simpler approach: use a “golden set” of questions and employ keyword detection in the responses to gauge if they align with your desired outcome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Latency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a “DIY” scenario, users don’t want to wait 1 minute for a response while holding a power drill. Higher-performing models are often slower, whereas “Nano” or “Flash” models are optimized for speed, but may sacrifice some accuracy. Therefore, when selecting a model, you must prioritize response time for the end-user while balancing overall cost, efficiency, and performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Context Length and Memory&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As users engage in longer sessions, the “memory” of the conversation grows. Models have different limits on how much information they can “remember” (context length). If your project requires analyzing long manuals or maintaining a multi-day conversation, you may need a model with a larger context window, which often comes at a higher price point, as well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Caching Efficiency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Modern APIs allow you to save money by “caching” frequent inputs. In our DIYgpt exercise, I assumed &lt;strong&gt;10% of inputs were cached&lt;/strong&gt;, which significantly reduced the cost for repetitive instructions. The actual caching percentage is highly dependent on the domain and can fluctuate. Continuous monitoring is essential to establish a more accurate, realistic number over time.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h1&gt;

&lt;p&gt;Starting with costs wasn’t just an academic exercise - it was a lesson in viability. Whether you are building a simple assistant or a complex agentic system, understanding your “cost per query” is the first step in ensuring your AI transformation is sustainable in the long run. The &lt;strong&gt;DIYgpt&lt;/strong&gt; case study demonstrated that the cost of AI is not merely a background detail but a decisive factor in a project’s survival. As our financial analysis revealed, the choice of model can result in a “staggering” disparity in daily burn rates - ranging from as low as &lt;strong&gt;$13.84&lt;/strong&gt; for GPT−4.1 nano to &lt;strong&gt;$276.90&lt;/strong&gt; for o4-mini, daily cost.&lt;/p&gt;

&lt;p&gt;However, the lowest price tag does not guarantee the best outcome. True optimization requires looking “beyond the numbers” to balance cost efficiency with user experience. In a real-world scenario - such as a user waiting for instructions while holding a heavy power drill - metrics like &lt;strong&gt;latency&lt;/strong&gt; and &lt;strong&gt;accuracy&lt;/strong&gt; become just as valuable as the raw cost per token.&lt;/p&gt;

&lt;p&gt;Ultimately, this exercise proves that sustainable AI transformation is not just about code; it is about architecture and economics. By leveraging technical efficiencies like &lt;strong&gt;caching&lt;/strong&gt; and selecting models that offer the right accuracy for the specific domain, developers can bridge the gap between a cool prototype and a scalable business solution. As we move toward more complex Agentic systems, where autonomous loops increase token consumption, this ability to forecast and manage the &lt;strong&gt;“burn”&lt;/strong&gt; will be the defining characteristic of successful organizational transformation.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;references&quot;&gt;References:&lt;/h4&gt;

&lt;p&gt;The following price list is reported on the &lt;a href=&quot;https://openai.com/api/pricing/&quot;&gt;OpenAI pricing website&lt;/a&gt; as of the time of writing this document.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Model&lt;/strong&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Input ($/1M tokens)&lt;/strong&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Cached Input ($/1M tokens)&lt;/strong&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Output ($/1M tokens)&lt;/strong&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;GPT-4.1&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;3.00&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0.75&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;12.00&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;GPT-4.1 mini&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0.80&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0.20&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;3.20&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;GPT-4.1 nano&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0.20&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0.05&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0.80&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;o4-mini&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;4.00&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1.00&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;16.00&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;This is the pricing reported for Gemini models through &lt;a href=&quot;https://cloud.google.com/vertex-ai/generative-ai/pricing&quot;&gt;Vertex AI platform&lt;/a&gt;. It was assumed that the query input context is less than 200K tokens.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Model&lt;/strong&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Price (/1M tokens) &amp;lt;= 200K input tokens&lt;/strong&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Price (/1M tokens) &amp;lt;= 200K cached input tokens&lt;/strong&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Output ($/1M tokens)&lt;/strong&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Gemini 3 Pro Preview&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0.2&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;12&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Gemini 3 Flash Preview&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0.5&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;0.05&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;3&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        <pubDate>Mon, 02 Feb 2026 21:05:55 +0000</pubDate>
        <link>https://giofontana.github.io/blog/2026-02-03-cost-of-ai/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/2026-02-03-cost-of-ai/</guid>
      </item>
    
      <item>
        <title>Automate VM golden image builds for OpenShift with Packer</title>
        <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href=&quot;https://developers.redhat.com/articles/2025/11/07/automate-vm-golden-image-builds-openshift-packer&quot;&gt;https://developers.redhat.com&lt;/a&gt; on November 07, 2025.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In any virtualized environment, maintaining consistency across virtual machines (VMs) is a major challenge. Golden images (pre-configured VM templates) are the industry-standard solution. They ensure every new VM comes with the correct OS settings, security patches, and monitoring tools baked in. But how do you create and manage these images without tedious manual work?&lt;/p&gt;

&lt;p&gt;This is where Packer comes into the picture. Packer is a tool that automates the creation of identical machine images for multiple platforms from a single template. In this article, we’ll show you how to use Packer with the KVM plugin to build golden images specifically for Red Hat OpenShift Virtualization.&lt;/p&gt;

&lt;h2 id=&quot;packer&quot;&gt;Packer&lt;/h2&gt;
&lt;p&gt;Packer allows you to create images from a single source template, described using either HashiCorp Configuration Language (HCL) or JSON. It provides a large collection of plugins to create machines and images for different platforms, such AWS, Azure, VMware, KVM, and others. Refer to the &lt;a href=&quot;https://www.packer.io/plugins&quot;&gt;Packer Integrations&lt;/a&gt; page for more information.&lt;/p&gt;

&lt;h2 id=&quot;red-hat-openshift-virtualization&quot;&gt;Red Hat OpenShift Virtualization&lt;/h2&gt;
&lt;p&gt;Red Hat OpenShift Virtualization, built on KubeVirt, is a feature of Red Hat OpenShift that allows you to run and manage traditional virtual machines alongside containers on a single, hybrid cloud platform. This unification of both containerized and virtualized workloads simplifies management and enables a consistent operational experience across your entire infrastructure.&lt;/p&gt;

&lt;h3 id=&quot;packer--openshift-virtualization-better-together&quot;&gt;Packer + OpenShift Virtualization: Better together&lt;/h3&gt;
&lt;p&gt;Through the KVM plugin and with a few simple commands, you can make your golden image ready on OpenShift Virtualization. The &lt;a href=&quot;https://github.com/redhat-cop/openshift-virt-packer&quot;&gt;GitHub repository&lt;/a&gt; includes a collection of examples for creating images for various operating systems, including Linux (Fedora, RHEL 9) and Windows.&lt;/p&gt;

&lt;p&gt;Later in this article, we will dig into one of these examples, but the general workflow is simple. You define a Packer template (HCL) that specifies the operating system, configuration scripts, and other customizations. Packer then uses this template to build the VM image automatically using KVM. The output will be a qcow2 image file for you to upload to OpenShift Virtualization and use as a golden image for your VM provisioning.&lt;/p&gt;

&lt;p&gt;Once Packer completes the qcow2 file creation, uploading it to OpenShift and setting it as a bootable image are steps easily automated as part of a pipeline or with automation tools like Red Hat Ansible Automation Platform or Terraform.&lt;/p&gt;

&lt;h3 id=&quot;key-features-and-benefits&quot;&gt;Key features and benefits:&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Automation:&lt;/strong&gt; Eliminate tedious manual image creation, saving time and preventing configuration drift.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Customization:&lt;/strong&gt; Easily embed specific software, user configurations, and security settings directly into the image.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Performance optimization:&lt;/strong&gt; Build images with the best drivers for your environment, like using virtio drivers for Windows on KVM to achieve near-native performance.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reduced risk:&lt;/strong&gt; Automating the image creation process ensures a standardized and less error-prone approach compared to manual methods.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Simple integration:&lt;/strong&gt; The build process outputs a qcow2 file that can upload to OpenShift Virtualization with a few simple commands, perfect for CI/CD pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;
&lt;p&gt;The project’s GitHub repository provides detailed instructions on how to get started. Here’s a quick overview of the steps involved:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Clone the repository:&lt;/strong&gt; Obtain a local copy of the project.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Install Packer and KVM:&lt;/strong&gt; Install Packer on your system.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Choose an example:&lt;/strong&gt; The repository includes examples for different operating systems.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Build the image:&lt;/strong&gt; Run the Packer build command to create your custom VM image.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Upload to OpenShift:&lt;/strong&gt; Use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;virtctl&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oc&lt;/code&gt; command-line tool to upload the image to OpenShift Virtualization.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s walk through the Windows Server 2019 example to see how the automation comes together. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;windows2019.pkr.hcl&lt;/code&gt; Packer template defines the process and orchestrates the entire build.&lt;/p&gt;

&lt;p&gt;The source block defines the crucial first step, where Packer creates and attaches a virtual CD-ROM to the new VM. This CD contains all the files needed for an unattended installation:&lt;/p&gt;

&lt;div class=&quot;language-hcl highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# This creates a virtual CD with our automation files and drivers.&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;cd_files&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;./autounattend.xml&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;./scripts/Configure-WinRM.ps1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;./virtio/&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;cd_label&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;PACKERDRV&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# A label for our driver CD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Windows installer then uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autounattend.xml&lt;/code&gt; file from this virtual CD as its answer file to automate the setup. Inside this XML file, we point the installer to the virtio drivers, also located on the virtual CD (which typically mounts as the E: drive). This allows Windows to use the high-performance KVM-native storage and network drivers right from the start.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;DriverPaths&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PathAndCredentials&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;wcm:action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;add&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;wcm:keyValue=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path&amp;gt;&lt;/span&gt;E:\virtio\viostor\2k19\amd64&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Path&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PathAndCredentials&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PathAndCredentials&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;wcm:action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;add&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;wcm:keyValue=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Path&amp;gt;&lt;/span&gt;E:\virtio\NetKVM\2k19\amd64&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Path&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PathAndCredentials&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DriverPaths&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After the OS installation is complete, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autounattend.xml&lt;/code&gt; file has one more critical job. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FirstLogonCommands&lt;/code&gt; section automatically runs our PowerShell script.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;FirstLogonCommands&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;SynchronousCommand&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;wcm:action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;add&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;CommandLine&amp;gt;&lt;/span&gt;powershell.exe -ExecutionPolicy Bypass -File E:\Configure-WinRM.ps1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/CommandLine&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Description&amp;gt;&lt;/span&gt;Install and Configure SSH&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Description&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Order&amp;gt;&lt;/span&gt;1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Order&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/SynchronousCommand&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/FirstLogonCommands&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This script configures Windows Remote Management (WinRM), which opens a communication channel back to Packer. Once Packer can connect to the VM via WinRM, it takes over to run post-install configurations. In this example, it executes scripts to install Windows updates before finalizing the qcow2 image.&lt;/p&gt;

&lt;h2 id=&quot;from-image-to-openshift-vm&quot;&gt;From image to OpenShift VM&lt;/h2&gt;
&lt;p&gt;Once Packer creates the qcow2 file, the final step is to make it available in OpenShift Virtualization:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Upload the image:&lt;/strong&gt; First, you upload the qcow2 file, which creates a Persistent Volume Claim (PVC) that serves as a bootable volume for your new virtual machines.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Create a template (optional):&lt;/strong&gt; For better reusability, you can create a custom VM template that points to this new bootable volume. This allows developers and operators to provision new, fully configured VMs with just a few clicks from the OpenShift console.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For organizations managing multiple OpenShift clusters, distributing and managing these golden images can be streamlined using Red Hat Advanced Cluster Management (RHACM). RHACM policies can automate the distribution of these templates and their associated resources across your entire fleet, ensuring consistency and saving significant operational effort.&lt;/p&gt;

&lt;p&gt;The repository’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;README.md&lt;/code&gt; file provides detailed instructions for this, including an example of how to use Red Hat Advanced Cluster Management policies to distribute these images across multiple clusters.&lt;/p&gt;

&lt;h2 id=&quot;simplify-your-vm-images-on-openshift&quot;&gt;Simplify your VM images on OpenShift&lt;/h2&gt;
&lt;p&gt;By automating the VM image creation process, teams improve efficiency, consistency, and scalability. Whether you’re a developer, a system administrator, or a DevOps engineer, the powerful combination of Packer and OpenShift Virtualization can help you streamline your workflows and make the most of your platform.&lt;/p&gt;

&lt;p&gt;If you’re ready to streamline how you manage VM images on OpenShift, dive into the &lt;a href=&quot;https://github.com/redhat-cop/openshift-virt-packer&quot;&gt;openshift-virt-packer repository&lt;/a&gt;. Clone it, run an example, and start building your own automated image pipeline today with Packer and OpenShift Virtualization. You can also find more information in the &lt;a href=&quot;https://docs.redhat.com/&quot;&gt;Packer and OpenShift Virtualization - Managing VMs&lt;/a&gt; documentation.&lt;/p&gt;

&lt;p&gt;To learn more about Red Hat OpenShift Virtualization, check out the &lt;a href=&quot;https://www.redhat.com/en/resources/15-reasons-to-adopt-openshift-virtualization-ebook&quot;&gt;15 reasons to adopt Red Hat OpenShift Virtualization&lt;/a&gt; e-book and explore how to build a migration plan with Red Hat experts through the &lt;a href=&quot;https://www.redhat.com/en/services/consulting/virtualization-migration-assessment&quot;&gt;Virtualization Migration Assessment&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Thu, 06 Nov 2025 21:05:55 +0000</pubDate>
        <link>https://giofontana.github.io/blog/automate-vm-golden-image-openshift-virt-packer/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/automate-vm-golden-image-openshift-virt-packer/</guid>
      </item>
    
      <item>
        <title>Effective observability with Red Hat build of OpenTelemetry | Red Hat Developer</title>
        <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href=&quot;https://developers.redhat.com/articles/2025/09/03/effective-observability-red-hat-build-opentelemetry&quot;&gt;https://developers.redhat.com&lt;/a&gt; on September 3, 2025.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In today’s complex IT environments, monitoring and understanding the health and performance of your applications and infrastructure is critical. The &lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html-single/red_hat_build_of_opentelemetry/index&quot;&gt;Red Hat build of OpenTelemetry&lt;/a&gt;, which can be installed in Red Hat OpenShift, provides a powerful framework for collecting and exporting telemetry data, enabling comprehensive metrics and logs reporting. In this article, we will explore the benefits and capabilities of using the Red Hat build of OpenTelemetry for effective observability.&lt;/p&gt;

&lt;p&gt;OpenTelemetry is an open source project under the Cloud Native Computing Foundation (CNCF) that provides a set of APIs, libraries, agents, and collectors to capture distributed traces, metrics, and logs. Red Hat build of OpenTelemetry is a distribution of the upstream OpenTelemetry project, built and supported by Red Hat.&lt;/p&gt;

&lt;h1 id=&quot;collecting-metrics-with-red-hat-build-of-opentelemetry&quot;&gt;Collecting metrics with Red Hat build of OpenTelemetry&lt;/h1&gt;
&lt;p&gt;Metrics provide insights into the performance and health of your applications. The Red Hat build of OpenTelemetry can collect various types of metrics, including:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;System metrics: CPU usage, memory consumption, disk I/O, etc.&lt;/li&gt;
  &lt;li&gt;Application metrics: Request latency, error rates, and throughput.&lt;/li&gt;
  &lt;li&gt;Custom metrics: Business-specific metrics tailored to your application needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can export these metrics to various monitoring systems, such as Prometheus, for visualization and analysis.&lt;/p&gt;

&lt;h1 id=&quot;key-benefits-of-red-hat-build-of-opentelemetry&quot;&gt;Key benefits of Red Hat build of OpenTelemetry&lt;/h1&gt;
&lt;p&gt;The Red Hat build of OpenTelemetry simplifies the collection and management of telemetry data, offering several key advantages:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Standardization: Provides a unified way to collect and export data to third-party tools, reducing vendor lock-in.&lt;/li&gt;
  &lt;li&gt;Scalability: Designed to handle large volumes of data in complex environments.&lt;/li&gt;
  &lt;li&gt;Flexibility: Supports multiple data formats and backends, allowing integration with existing monitoring tools.&lt;/li&gt;
  &lt;li&gt;Red Hat support: Offers enterprise-grade support and maintenance from Red Hat, integration with Red Hat Observability UI and supported integration with third-party vendors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;using-red-hat-build-of-opentelemetry&quot;&gt;Using Red Hat build of OpenTelemetry&lt;/h1&gt;
&lt;p&gt;You can seamlessly integrate Red Hat build of OpenTelemetry with existing monitoring and logging systems. The OpenTelemetry collector acts as a central hub for receiving, processing, and exporting telemetry data. OpenTelemetry Collector is composed of the following components.&lt;/p&gt;

&lt;h2 id=&quot;receivers&quot;&gt;Receivers:&lt;/h2&gt;
&lt;p&gt;Receives telemetry data from different sources.
Supported receivers include: OTLP, Jaeger, Host Metrics, Kubernetes Objects Receiver, and many others. Refer to the &lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/red_hat_build_of_opentelemetry/configuring-the-collector#otel-collector-receivers&quot;&gt;full list of supported receivers&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;exporters&quot;&gt;Exporters:&lt;/h2&gt;

&lt;p&gt;Send telemetry data to various backends.
Supported exporters include: OTLP, OTLP HTTP, Prometheus, and others. Refer to the &lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/red_hat_build_of_opentelemetry/configuring-the-collector#otel-collector-exporters&quot;&gt;full list of supported exporters&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;processors&quot;&gt;Processors:&lt;/h2&gt;
&lt;p&gt;Manipulate data between received and sent (optional but very helpful).
Supported processors include: batch, attributes, kubernetes attributes, and others. Refer to the &lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/red_hat_build_of_opentelemetry/configuring-the-collector#otel-collector-processors&quot;&gt;full list of supported processors&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href=&quot;https://opentelemetry.io/docs/collector/architecture/&quot;&gt;OpenTelemetry architecture documentation&lt;/a&gt; for more information.&lt;/p&gt;

&lt;h1 id=&quot;getting-started-with-red-hat-build-of-opentelemetry&quot;&gt;Getting started with Red Hat build of OpenTelemetry&lt;/h1&gt;
&lt;p&gt;To get started with the Red Hat build of OpenTelemetry, you can follow these general steps.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Deploy the Red Hat build of OpenTelemetry operator: Deploy the Red Hat build of OpenTelemetry operator in your OpenShift cluster.&lt;/li&gt;
  &lt;li&gt;Deploy the OpenTelemetry collector: Deploy the OpenTelemetry Collector in your environment.&lt;/li&gt;
  &lt;li&gt;Visualize your data: Use tools like Grafana to visualize and analyze your metrics and logs.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;deploy-of-red-hat-build-of-opentelemetry-operator&quot;&gt;Deploy of Red Hat build of OpenTelemetry operator&lt;/h1&gt;
&lt;p&gt;The deployment process of the Red Hat build of OpenTelemetry operator is very straightforward.
In your OpenShift cluster, follow these steps:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Go to Operators OperatorHub and search for the Red Hat build of OpenTelemetry operator.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Select the Red Hat build of OpenTelemetry Operator that is provided by Red Hat and click in Install. Leave the default presets:
a) Update channel: stable
b) All namespaces on the cluster
c) Installed Namespace: openshift-operators
d) Update approval: Automatic&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;In the Details tab of the Installed Operator page, under ClusterServiceVersion details, verify that the installation Status is Succeeded.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;deploy-opentelemetry-collector&quot;&gt;Deploy OpenTelemetry collector&lt;/h1&gt;
&lt;p&gt;The OpenTelemetry collector is where all the magic happens. In the collector, you will define all the receivers, processors, and exporters you want in your environment. Let’s dig into the receivers that make more sense to use in a Kubernetes environment.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/red_hat_build_of_opentelemetry/configuring-the-collector#hostmetrics-receiver_otel-collector-receivers&quot;&gt;Host metrics receiver&lt;/a&gt;: Designed to collect performance metrics directly from the underlying host operating system. This includes vital information about CPU utilization, memory consumption, disk I/O, network traffic, and other system-level statistics. By monitoring these metrics, users gain insights into the health and resource utilization of the nodes running their Kubernetes cluster. This receiver is crucial for identifying potential bottlenecks or resource exhaustion at the host level.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/red_hat_build_of_opentelemetry/configuring-the-collector#k8sobjectsreceiver-receiver_otel-collector-receivers&quot;&gt;Kubernetes objects receiver&lt;/a&gt;: Provides a way to observe the state of Kubernetes resources. It can monitor various Kubernetes objects such as pods, nodes, deployments, services, and more. The receiver captures metadata and status information about these objects, allowing users to track their health, configuration, and lifecycle events. This is essential for understanding the overall health and configuration of the Kubernetes cluster itself.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/red_hat_build_of_opentelemetry/configuring-the-collector#kubeletstats-receiver_otel-collector-receivers&quot;&gt;Kubelet stats receiver&lt;/a&gt;: Focuses on collecting detailed performance metrics directly from the Kubelet, the primary node agent in Kubernetes. These metrics offer granular insights into the resource usage and performance of individual pods and containers running on a specific node. Information such as CPU and memory usage per container, network statistics, and disk I/O at the pod level can be obtained through this receiver, enabling fine-grained monitoring and troubleshooting of application performance within the Kubernetes environment.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/red_hat_build_of_opentelemetry/configuring-the-collector#k8scluster-receiver_otel-collector-receivers&quot;&gt;Kubernetes cluster receiver&lt;/a&gt;: The Kubernetes cluster receiver offers a broader view of the Kubernetes cluster’s health and performance. It aggregates information from various Kubernetes components, providing insights into the control plane’s operation, such as the API server’s health, scheduler performance, and controller manager activity. This receiver helps in understanding the overall stability and responsiveness of the Kubernetes control plane.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/red_hat_build_of_opentelemetry/configuring-the-collector#filelog-receiver_otel-collector-receivers&quot;&gt;Filelog receiver&lt;/a&gt;: For log collection, the filelog receiver is a versatile option. It allows the OpenTelemetry collector to tail log files present on the file system of the nodes. This is particularly useful for collecting application logs written to files, as well as logs from other services running on the Kubernetes nodes. The receiver supports various configuration options for specifying file paths, log formats, and multiline log processing.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/red_hat_build_of_opentelemetry/configuring-the-collector#journald-receiver_otel-collector-receivers&quot;&gt;Journald receiver&lt;/a&gt;: Specifically designed for systems utilizing systemd’s journal for logging. It enables the OpenTelemetry collector to directly ingest logs from the systemd journal. This is beneficial in modern Linux distributions where systemd is the default init system, providing a centralized and structured way to collect system and application logs.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/red_hat_build_of_opentelemetry/configuring-the-collector#kubernetesevents-receiver_otel-collector-receivers&quot;&gt;Kubernetes events receiver&lt;/a&gt;: Allows the OpenTelemetry collector to capture Kubernetes events. These events provide valuable insights into significant occurrences within the cluster, such as pod creation, deletion, scaling events, and resource issues. By collecting and analyzing Kubernetes events, users can gain a better understanding of the dynamics of their cluster and troubleshoot issues proactively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Strategically select the receivers that make sense to your use case. You might not need all of them, but enabling the receivers mentioned previously will provide a very comprehensive metrics and logs reporting system tailored to Kubernetes environments. By selecting only specific receivers, you will keep the footprint of opentelemetry-collector low, allowing you to deploy the collector even in environments with limited resources.&lt;/p&gt;

&lt;h1 id=&quot;pipelines&quot;&gt;Pipelines&lt;/h1&gt;
&lt;p&gt;A pipeline defines the complete lifecycle of telemetry data. This journey begins with the reception of data from various sources, continues through optional processing stages where the data can be transformed, enriched, or filtered, and culminates in the export of the data to one or more back-end destinations for storage, visualization, or analysis.
A pipeline typically looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    service:
      pipelines:
        metrics:
          receivers:
            - hostmetrics
            - kubeletstats
            - k8s_cluster
          processors:
            - k8sattributes
          exporters:
            - debug
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are various types of pipelines:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Metrics: These pipelines are designed to handle numerical measurements captured at specific points in time. Metrics are essential for monitoring the performance and resource utilization of systems and applications. They often include aggregated data and can be used to create dashboards and set up alerts.&lt;/li&gt;
  &lt;li&gt;Logs: These pipelines deal with textual records of events that occur within a system or application. Logs provide detailed information about the behavior and state of software and infrastructure. Effective log management involves collecting, processing, and storing logs in a structured and searchable manner.&lt;/li&gt;
  &lt;li&gt;Traces: These pipelines are concerned with tracking the execution of requests as they propagate through distributed systems. A trace represents the end-to-end journey of a request, consisting of individual spans that represent specific operations or calls. Tracing is crucial for understanding the flow of execution, identifying performance bottlenecks, and diagnosing issues in microservice architectures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these pipeline types has its own set of specialized receivers, processors, and exporters that are tailored to the specific characteristics of the telemetry data they handle. By configuring pipelines appropriately for metrics, logs, and traces, users can gain comprehensive observability into their applications and infrastructure using the Red Hat build of OpenTelemetry.&lt;/p&gt;

&lt;p&gt;Now that we know how a collector works, let’s define a collector that will have all the receivers previously mentioned.&lt;/p&gt;

&lt;h1 id=&quot;receivers-1&quot;&gt;Receivers&lt;/h1&gt;

&lt;p&gt;The full collector is available &lt;a href=&quot;https://github.com/giofontana/rh-build-opentelemetry/blob/main/manifests/overlays/all/opentelemetry-collector.yaml&quot;&gt;here&lt;/a&gt;. The most important pieces are the following.&lt;/p&gt;

&lt;h2 id=&quot;host-metricsreceiver&quot;&gt;Host metrics receiver&lt;/h2&gt;

&lt;p&gt;Available scrapers for host metrics:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;cpu: This scraper collects CPU utilization metrics, including information about user, system, idle, and wait times. Analyzing CPU metrics helps identify periods of high CPU load, which can indicate performance issues or the need for resource scaling.&lt;/li&gt;
  &lt;li&gt;memory: The memory scraper gathers information about memory usage, such as total, used, free, cached, and buffered memory. Monitoring memory metrics is critical for detecting memory leaks, insufficient memory allocation, or excessive memory consumption by applications.&lt;/li&gt;
  &lt;li&gt;disk: This scraper collects disk I/O metrics, providing insights into read and write operations, throughput, and latency for the host’s disks. Analyzing disk metrics can help identify slow disks or I/O bottlenecks that might be affecting application performance.&lt;/li&gt;
  &lt;li&gt;load: The load scraper collects system load averages (1-minute, 5-minute, and 15-minute averages), which provide a general indication of system utilization. High load averages can suggest that the system is overutilized.&lt;/li&gt;
  &lt;li&gt;filesystem: This scraper gathers metrics related to file system usage, including total size, used space, and available space for each mounted file system. Monitoring file system usage helps prevent disks from becoming full, which can lead to application failures.&lt;/li&gt;
  &lt;li&gt;paging: The paging scraper collects metrics about the host’s swap or page file usage, such as swap in and swap out rates. Excessive paging activity can indicate that the system is experiencing memory pressure.&lt;/li&gt;
  &lt;li&gt;processes: This scraper provides a count of the total number of processes running on the host.
process: This scraper allows for the collection of detailed metrics about specific processes running on the host. It typically requires further configuration to specify which processes to monitor and the specific metrics to collect (e.g., CPU usage, memory usage, I/O).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    receivers:
      hostmetrics:
        collection_interval: 60s
        initial_delay: 1s
        root_path: /
        scrapers: 
          cpu: {}
          memory: {}
          disk: {}
          load: {}
          filesystem: {}
          paging: {}
          processes: {}
          process: {}    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;kubernetes-objects&quot;&gt;Kubernetes objects&lt;/h2&gt;

&lt;p&gt;Monitored objects:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;name: pods: This configuration instructs the receiver to collect information about Kubernetes pod objects.
    &lt;ul&gt;
      &lt;li&gt;mode: pull: The pull mode indicates that the receiver will periodically query the Kubernetes API server to retrieve the current state of all pod objects in the cluster.&lt;/li&gt;
      &lt;li&gt;interval: 60s: This parameter specifies how frequently the receiver will poll the API server for pod information. In this case, it will check every 60 seconds.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;name: events: This configuration tells the receiver to collect Kubernetes event objects.
    &lt;ul&gt;
      &lt;li&gt;mode: watch: The watch mode utilizes the Kubernetes watch API, which allows the receiver to receive near real-time notifications whenever events occur in the cluster. This is more efficient than periodically polling, as the receiver only receives updates when changes happen.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;     k8sobjects:
        auth_type: serviceAccount
        objects:
          - name: pods
            mode: pull 
            interval: 60s
          - name: events
            mode: watch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;kubeletstats&quot;&gt;Kubeletstats&lt;/h2&gt;
&lt;p&gt;Configuration parameters:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;endpoint: This parameter defines the URL of the Kubelet API endpoint. The configuration uses the environment variable K8S_NODE_NAME to dynamically determine the hostname or IP address of the current node. The standard Kubelet port for secure communication is 10250. The https:// scheme ensures that communication with the Kubelet is encrypted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;      kubeletstats:
        collection_interval: 60s
        auth_type: &quot;serviceAccount&quot;
        endpoint: &quot;https://${env:K8S_NODE_NAME}:10250&quot;
        insecure_skip_verify: true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;k8s_cluster&quot;&gt;K8s_cluster&lt;/h2&gt;

&lt;p&gt;Configuration parameters:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;distribution: This parameter specifies the type of Kubernetes distribution being monitored. In this example, it is set to openshift, indicating that the metrics and logs are being collected from an OpenShift cluster. This information can be crucial for tailoring collection methods and interpreting cluster-specific data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;      k8s_cluster:
        distribution: openshift
        collection_interval: 60s  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;k8s_events&quot;&gt;K8s_events&lt;/h2&gt;
&lt;p&gt;To limit the scope of event collection to specific namespaces, the namespaces parameter can be defined as a list of namespace names. This allows for focused monitoring and reduces the volume of event data being processed. In this example, the namespaces are commented out, so all namespaces will be collected.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;      k8s_events:
        namespaces: [project1, project2]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;journald-configuration-breakdown&quot;&gt;Journald configuration breakdown:&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;files: /var/log/journal/*/: This parameter specifies the path to the journal files that the collector should monitor. The wildcard characters * allow for matching across different journal file directories and individual journal files within those directories. This ensures comprehensive log collection from all persistent journal logs.&lt;/li&gt;
  &lt;li&gt;priority: info: This setting defines the minimum severity level of logs to be collected. By setting it to info, the collector will gather all logs with a priority of info and higher (e.g., warning, err, crit, alert, emerg). This allows for filtering out less critical debug-level messages, reducing noise and focusing on more important events.&lt;/li&gt;
  &lt;li&gt;units: This section allows for filtering logs based on specific systemd units.
    &lt;ul&gt;
      &lt;li&gt;kubelet: This entry specifies that logs originating from the kubelet systemd unit should be collected. The kubelet is the primary “node agent” in Kubernetes, responsible for running containers on a worker node. Collecting its logs is crucial for monitoring the health and status of pods and the node.&lt;/li&gt;
      &lt;li&gt;crio: This entry indicates the collection of logs from the crio (Container Runtime Interface) unit. CRI-O is a lightweight container runtime for Kubernetes, and its logs provide insights into container lifecycle events, image management, and other runtime-related activities.&lt;/li&gt;
      &lt;li&gt;init.scope: This entry configures the collection of logs from the init.scope unit. init.scope represents the system’s initialization process and often contains essential boot-related information and early system messages.&lt;/li&gt;
      &lt;li&gt;dnsmasq: This entry specifies the collection of logs from the dnsmasq unit. dnsmasq is a lightweight DNS forwarder and DHCP server commonly used in Kubernetes clusters for internal DNS resolution. Monitoring its logs can be helpful for diagnosing DNS-related issues within the cluster.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;all: true: This boolean parameter, when set to true, instructs the collector to gather logs from all other journal entries that are not explicitly excluded by other filters (i.e., the units list). This acts as a catch-all to ensure that important system-level logs beyond the specified units are also captured.&lt;/li&gt;
  &lt;li&gt;retry_on_failure: This section configures how the collector should handle failures during log collection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;      journald:
        files: /var/log/journal/*/*
        priority: info 
        units:
          - kubelet
          - crio
          - init.scope
          - dnsmasq
        all: true
        retry_on_failure:
          enabled: true
          initial_interval: 1s
          max_interval: 60s
          max_elapsed_time: 5m
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can find the complete OpenTelemetry collector on &lt;a href=&quot;https://github.com/giofontana/rh-build-opentelemetry/blob/main/manifests/overlays/all/opentelemetry-collector.yaml&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Depending on the receivers used, the collector needs different permissions. You will find the permissions in &lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.18/html/red_hat_build_of_opentelemetry/configuring-the-collector#otel-collector-receivers&quot;&gt;the documentation of each receiver&lt;/a&gt;. For the receivers listed above, the necessary permissions are available at the &lt;a href=&quot;https://github.com/giofontana/rh-build-opentelemetry/tree/main/manifests/base&quot;&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install the collector with all necessary permissions, run the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/giofontana/rh-build-opentelemetry.git
cd rh-build-opentelemetry
oc apply -k manifests/overlays/debug
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;testing-the-opentelemetry-collector&quot;&gt;Testing the OpenTelemetry collector&lt;/h1&gt;
&lt;p&gt;Initially, our collector only logs the metrics collected by the receivers using the debug exporter. To fully leverage the collected metrics, we will now change the configuration to use OTLP/HTTP exporter, enabling the collector to send the gathered metrics data to a remote system accessible via HTTP using the OpenTelemetry Protocol (OTLP).&lt;br /&gt;
For demonstration and testing of the OpenTelemetry setup, we will deploy the following systems in a Red Hat Enterprise Linux virtual machine. These systems are essential for illustrating the complete data flow, from collection through processing to visualization and storage. The following systems are deployed in the VM:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Grafana: Grafana provides a robust platform for creating and displaying dynamic dashboards. It will be configured to connect to the metrics storage solution and present the collected data in a visually intuitive format, enabling detailed analysis and monitoring.&lt;/li&gt;
  &lt;li&gt;Mimir: Mimir is an open source time-series database system that serves as the primary storage for the metrics exported by the OpenTelemetry collector. Mimir’s scalable architecture and efficient query engine make it ideal for handling large volumes of time-series data.&lt;/li&gt;
  &lt;li&gt;Loki: Loki is a horizontally scalable, highly available, multi-tenant log aggregation system. It will be used to manage and store log data, complementing the metrics data stored in Mimir.&lt;/li&gt;
  &lt;li&gt;Promtail: Promtail is an agent that collects logs from various sources and sends them to Loki. It will be configured to scrape logs from the OpenTelemetry collector and other relevant system components, ensuring that all relevant log data is captured and accessible in Loki.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Detailed, step-by-step instructions on how to deploy these systems in a virtual machine are not the scope of this article, but you can refer to the &lt;a href=&quot;https://github.com/giofontana/rh-build-opentelemetry/blob/main/mimir/README.md&quot;&gt;documentation&lt;/a&gt; for more information.&lt;br /&gt;
With Mimir up and running, we will now reconfigure the OpenTelemetry collector to send metrics to it. To do so, change the endpoint of spec.config.exporters.otlphttp.endpoint to reflect your environment:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#Change line highlighted below:
vi manifests/overlays/all/opentelemetry-collector.yaml
   exporters:
      debug:
        verbosity: basic
      otlphttp:
        endpoint: &apos;http://10.1.1.100:9009/otlp&apos; #CHANGE IP
        tls:
          insecure: true
      otlphttp/logs:
        endpoint: &apos;http://10.1.1.100:3100/otlp&apos; #CHANGE IP
        tls:
          insecure: true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You might need to add other parameters depending on TLS and other configurations you have on your external system. Check this &lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.18/html-single/red_hat_build_of_opentelemetry/index#otlp-http-exporter_otel-collector-exporters&quot;&gt;documentation&lt;/a&gt; for more information about configuration parameters.&lt;br /&gt;
With the opentelemetry-collector.yaml properly configured, you can deploy the new collector.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#Delete the existing one, if exists
oc delete OpenTelemetryCollector otel -n k8s-otel
#Deploy the new one
oc apply -k manifests/overlays/all/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To verify the setup, add a new data source to Grafana for Mimir as follows:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Access Grafana at http://&lt;IP&gt;:3000/&lt;/IP&gt;&lt;/li&gt;
  &lt;li&gt;Use admin / admin as the initial password. Set a new password for the admin user.&lt;/li&gt;
  &lt;li&gt;Go to Connections -&amp;gt; Add new connection.&lt;/li&gt;
  &lt;li&gt;Choose Prometheus and click Add new data source.&lt;/li&gt;
  &lt;li&gt;Enter the following details:
    &lt;ul&gt;
      &lt;li&gt;Name: Mimir&lt;/li&gt;
      &lt;li&gt;Prometheus server URL: http://&lt;ip&gt;:9009/prometheus&lt;/ip&gt;&lt;/li&gt;
      &lt;li&gt;Authentication: None&lt;/li&gt;
      &lt;li&gt;Skip TLS certificate validation&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, use the &lt;strong&gt;Explore&lt;/strong&gt; function. You should now see available metrics, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;container_cpu_time&lt;/code&gt; (Figure 1).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2025-09-03-effective-observability/figure_1_5.png.webp&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1: The Grafana Explore feature, with Mimir data source, shows a metric collected and sent by OpenTelemetry.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Do the same for Loki:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Go to “Connections” -&amp;gt; “Add new connection”.&lt;/li&gt;
  &lt;li&gt;Choose Prometheus and click “Add new data source”.&lt;/li&gt;
  &lt;li&gt;Enter the following details:
    &lt;ul&gt;
      &lt;li&gt;Name: Loki&lt;/li&gt;
      &lt;li&gt;Prometheus server URL: http://&lt;ip&gt;:3100&lt;/ip&gt;&lt;/li&gt;
      &lt;li&gt;Authentication: None&lt;/li&gt;
      &lt;li&gt;Skip TLS certificate validation&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, use the &lt;strong&gt;Explore&lt;/strong&gt; function. Select Loki, any filter (e.g., k8s_namespace_name=k8s-otel), and click on Run query, as shown in Figure 2.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2025-09-03-effective-observability/figure_2_4.png.webp&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2: The Grafana Explore feature, with Loki datasource, shows logs collected and sent by OpenTelemetry.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To test OpenTelemetry, you can import this dashboard: &lt;a href=&quot;https://grafana.com/grafana/dashboards/20376-opentelemetry-collector-hostmetrics-node-exporter/&quot;&gt;https://grafana.com/grafana/dashboards/20376-opentelemetry-collector-hostmetrics-node-exporter/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Dashboards -&amp;gt; New -&amp;gt; Import&lt;/strong&gt;.&lt;br /&gt;
Enter 20376 and click the Load button, as shown in Figure 3.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2025-09-03-effective-observability/figure_3_5.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3: This example of a Grafana dashboard shows graphs and statistics from metrics collected by OpenTelemetry.&lt;/em&gt;&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;Red Hat build of OpenTelemetry provides a powerful and flexible solution for comprehensive metrics and logs reporting in complex environments. By standardizing data collection, offering robust scalability, and providing enterprise-grade support from Red Hat, it simplifies observability and enables deeper insights into application and infrastructure health. With its seamless integration capabilities and a rich set of receivers, processors, and exporters, the Red Hat build of OpenTelemetry allows you to tailor your monitoring setup to meet your specific needs.&lt;br /&gt;
To fully leverage the capabilities of the Red Hat build of OpenTelemetry and enhance your monitoring strategy, explore the official &lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html-single/red_hat_build_of_opentelemetry/index&quot;&gt;documentation&lt;/a&gt; and community resources. Dive deeper into configuring collectors, setting up pipelines, and integrating with visualization tools like Grafana. You can find detailed information and getting started guides to help you implement and optimize your observability practices. Find out more about &lt;a href=&quot;https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/red_hat_build_of_opentelemetry/index&quot;&gt;Red Hat build of OpenTelemetry&lt;/a&gt; and &lt;a href=&quot;https://redhat.com/observability&quot;&gt;Red Hat OpenShift observability&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Tue, 02 Sep 2025 21:05:55 +0000</pubDate>
        <link>https://giofontana.github.io/blog/2025-09-03-effective-observability/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/2025-09-03-effective-observability/</guid>
      </item>
    
      <item>
        <title>Applied Data Science Program: Levaraging AI for Effective Decision Making</title>
        <description>&lt;p&gt;🎉 I did it! Thrilled to share this milestone with all of you! Now, I won’t be totally lost in AI conversations! 😂&lt;/p&gt;

&lt;p&gt;On a serious note, this course has truly transformed my understanding of artificial intelligence. Generative AI often feels like a magic box, and without grasping the fundamentals, it’s easy to get lost. This course has been a game-changer for me, covering everything from the foundations of Data Science, Data Analysis, and Visualization to Neural Networks and Recommendation Systems.&lt;/p&gt;

&lt;p&gt;Feel free to reach out if you’d like to know more about my experience!&lt;/p&gt;
</description>
        <pubDate>Mon, 29 Jul 2024 21:05:55 +0000</pubDate>
        <link>https://giofontana.github.io/blog/2024-07-30-applied-data-science-mit-program/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/2024-07-30-applied-data-science-mit-program/</guid>
      </item>
    
      <item>
        <title>New Book Announcement - OpenShift Multi-Cluster Management Handbook</title>
        <description>&lt;p&gt;It’s been a long road to get here, but we can finally make this announcement! Our new book OpenShift Multi-Cluster Management Handbook is going to be released soon! It is available for &lt;a href=&quot;https://www.amazon.com/dp/B0BG5B6MK2/&quot;&gt;preorder on Amazon already&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our initial goal with OpenShift Multi-Cluster Management Handbook was to cover a wide range of topics that we found relevant while performing OpenShift-related activities in our career. We have been working for years with a variety of different tasks, such as clusters deployment, architectural design, troubleshooting, and others related to OpenShift, during this time we were able to accumulate experiences and lessons learned, such as:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The main challenges many companies face when adopting public cloud;&lt;/li&gt;
  &lt;li&gt;Common pitfalls with OpenShift adoption;&lt;/li&gt;
  &lt;li&gt;Architectural best practices;&lt;/li&gt;
  &lt;li&gt;Security concerns and best practices;&lt;/li&gt;
  &lt;li&gt;Main personas that work with OpenShift, their expectations, challenges, and key responsibilities;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our intention with this book was not only to provide technical content to help you learn about new topics but also to cover some topics that you can usually only learn on the job, with practical experience. Additionally, you will discover some up-to-date materials that will assist you to survive in this world of several OpenShift clusters living on multiple platforms, from on-premise to the cloud, using tools that make up the OpenShift Plus offering - Red Hat Advanced Cluster Management, Advanced Cluster Security, and Quay.&lt;/p&gt;

&lt;p&gt;It was our first time writing a book, and we are not even native English speakers, so we can definitely say it was a real challenge for us! This book is the result of many late nights and intensive research. But writing was not the only challenge: our family and closest friends know some of the real challenges we had during this journey, just to name a few: we both have our roles changed at Red Hat, one of the writers was relocated to a different city, and the other moved even farther away - to another country! So many new things in our personal and professional lives - not once or twice that we considered giving up, but, as the wise &lt;a href=&quot;https://www.youtube.com/watch?v=h5SNAluOj6U&quot;&gt;Master Yoda says: “Do or do not, there is no try.”&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We persisted, and after several months, we finally can say we succeeded! Here it is, the finished book, we hope you enjoy it and find it useful on your journey! Although we would be quite grateful if it becomes a good-selling book, our major goal will have been accomplished and we will feel that the several late hours were worthwhile if it helped even just one person get better outcomes.&lt;/p&gt;

&lt;p&gt;So, we encourage you to &lt;a href=&quot;https://www.amazon.com/dp/B0BG5B6MK2/&quot;&gt;check out our book on Amazon&lt;/a&gt;. If you want to get in touch, add us to your social network, GitHub, and join &lt;a href=&quot;https://community.packt.com/register/&quot;&gt;Packt Community Platform&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Our social network:
&lt;a href=&quot;https://twitter.com/giofontana&quot;&gt;Giovanni Fontana (@giofontana) / Twitter&lt;/a&gt; | &lt;a href=&quot;https://twitter.com/rvspecora&quot;&gt;Rafael Pécora (@rvspecora) / Twitter&lt;/a&gt; 
&lt;a href=&quot;https://www.linkedin.com/in/gfontana/&quot;&gt;Giovanni Fontana - OpenShift Specialist Solution Architect - Red Hat / LinkedIn&lt;/a&gt; | &lt;a href=&quot;https://www.linkedin.com/in/rpecora/&quot;&gt;Rafael Pécora - Cloud Success Architect - Red Hat / LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;GitHub: &lt;a href=&quot;https://github.com/giofontana&quot;&gt;giofontana (Giovanni Fontana) · GitHub&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://github.com/pecorawal&quot;&gt;pecorawal (Rafael Pécora) · GitHub&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        <pubDate>Thu, 10 Nov 2022 21:05:55 +0000</pubDate>
        <link>https://giofontana.github.io/blog/2022-11-11-book-announcement/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/2022-11-11-book-announcement/</guid>
      </item>
    
      <item>
        <title>Nothing is permanent except change!</title>
        <description>&lt;p&gt;My family and I made a significant change in our lives in May of this year: We relocated from my home country of sunny and warm Brazil to the lovely, historical, and cold — at least for me! — New England, more specifically Massachusetts. It is difficult to leave behind friends, family, and my country, but we have decided to take on this challenge.&lt;/p&gt;

&lt;p&gt;During this transition period, we faced numerous challenges — I’m still wondering whether or not to write a separate post about it (what do you think?) — but difficulties ranging from financing and rental due to a lack of a credit score to unexpected emotional instability due to the distance from family and friends. I left home when I was 18 years old to attend university, and a few years later I moved to another city to work. I also had to travel quite frequently for work at some point in my life, and I assumed that because of these and other factors, it would not be as difficult, emotionally speaking, to deal with the many things that a change like this generates. But this time was different: seeing my son cry because he can’t play with his friends or stay close to our family hurts so much! I felt the weight of responsibility on my shoulders and wondered many times if I had made the right decision.&lt;/p&gt;

&lt;p&gt;But as time passes, we gradually overcome the challenges and adapt to the new reality. I feel that we will always miss our home country, but the experiences we have had here in these few months have been priceless! We were able to experience the intensity of summer here, as well as the splendor of nature in the fall, with its incredible red, yellow, and colorful leaves!&lt;/p&gt;

&lt;p&gt;We are now eagerly awaiting the arrival of winter with a mixture of excitement and anxiety; we are from a warm country, so perhaps this will be our most difficult challenge here?!?! I’m not sure, but that’s what we’re going to look into soon! :)&lt;/p&gt;

&lt;p&gt;So, how about you? How do you deal with life changes? Comment and share!&lt;/p&gt;
</description>
        <pubDate>Mon, 17 Oct 2022 21:05:55 +0000</pubDate>
        <link>https://giofontana.github.io/blog/2022-10-18-nothing-is-permanent/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/2022-10-18-nothing-is-permanent/</guid>
      </item>
    
      <item>
        <title>Deploying a Private OpenShift Cluster on AWS using Transit Gateway</title>
        <description>&lt;p&gt;These are the steps we are going to follow:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;VPCs creation;&lt;/li&gt;
  &lt;li&gt;Transit Gateway and route table configuration;&lt;/li&gt;
  &lt;li&gt;Deployment of a Red Hat OpenShift on AWS (ROSA) cluster using the OpenShift Cluster Manager on console.redhat.com;&lt;/li&gt;
  &lt;li&gt;Publishing DNS in the public VPC;&lt;/li&gt;
  &lt;li&gt;Deployment of a Jump server to access the private subnets.&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;vpcs-creation&quot;&gt;VPCs Creation&lt;/h1&gt;

&lt;p&gt;We are going to create 3 VPCs:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Egress VPC&lt;/strong&gt;: This VPC is the only public one, which has an Internet and NAT Gateway. All egress traffic flows through this VPC. Our jump server will also be in this VPC, as it is the only one with public internet access.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ROSA VPC&lt;/strong&gt;: Private VPC in which we are going to install the Red Hat OpenShift on AWS (ROSA) cluster.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Other VPC&lt;/strong&gt;: This VPC would represent other services that applications on OpenShift would be interacting with, such as Databases, APIs, etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The following table has the details of the VPCs we are going to create:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;VPC Name&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;CIDR block&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Subnets&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;egress-vpc&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.0.0.0/24&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;us-east-1a&lt;/strong&gt;&lt;br /&gt;egress-subnet-public1-us-east-1a - 10.0.0.0/28&lt;br /&gt;egress-subnet-private1-us-east-1a - 10.0.0.128/28&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;ocp-vpc&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.1.0.0/16&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;us-east-1a&lt;/strong&gt;&lt;br /&gt;ocp-subnet-private1-us-east-1a - 10.1.128.0/20&lt;br /&gt;&lt;strong&gt;us-east-1b&lt;/strong&gt;&lt;br /&gt;ocp-subnet-private2-us-east-1b - 10.1.144.0/20&lt;br /&gt;&lt;strong&gt;us-east-1c&lt;/strong&gt;&lt;br /&gt;ocp-subnet-private3-us-east-1c - 10.1.160.0/20&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;integration-vpc&lt;/strong&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10.2.0.0/16&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;us-east-1a&lt;/strong&gt;&lt;br /&gt;integration-subnet-private1-us-east-1a - 10.2.128.0/20&lt;br /&gt;&lt;strong&gt;us-east-1b&lt;/strong&gt;&lt;br /&gt;integration-subnet-private2-us-east-1b - 10.2.144.0/20&lt;br /&gt;&lt;strong&gt;us-east-1c&lt;/strong&gt;&lt;br /&gt;integration-subnet-private3-us-east-1c - 10.2.160.0/20&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Follow the steps below to create the VPCs:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Access your AWS account and navigate to the VPC feature of the desired region.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/01.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Click on &lt;strong&gt;Create VPC&lt;/strong&gt; button on the top right corner of the screen.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/02.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Fill out the form for the &lt;strong&gt;egress-vpc&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Resources to create&lt;/strong&gt;: VPC and more&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Name tag auto-generation&lt;/strong&gt;: egress&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;IPv4 CIDR block&lt;/strong&gt;: 10.0.0.0/24 (*)&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;IPv6 CIDR block&lt;/strong&gt;: No IPv6 CIDR block&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Number of Availability Zones (AZs)&lt;/strong&gt;: 1&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Number of public subnets&lt;/strong&gt;: 1&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Number of private subnets&lt;/strong&gt;: 1&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Public subnet CIDR block in us-east-1a&lt;/strong&gt;: 10.0.0.0/28 (*)&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Private subnet CIDR block in us-east-1a&lt;/strong&gt;: 10.0.0.128/28 (*)&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;NAT gateways ($)&lt;/strong&gt;: In 1 AZ&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;VPC endpoints&lt;/strong&gt;: S3 Gateway&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Enable DNS hostnames&lt;/strong&gt;: enabled&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Enable DNS resolution&lt;/strong&gt;: enabled&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;(*) You may customize the CIDR ranges as you need, just make sure there are no overlaps between the CIDRs of each VPCs and subnets.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/03.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Repeat the same thing to create the &lt;strong&gt;OpenShift VPC (ocp-vpc)&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Resources to create&lt;/strong&gt;: VPC and more&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Name tag auto-generation&lt;/strong&gt;: ocp&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;IPv4 CIDR block&lt;/strong&gt;: 10.1.0.0/16 (*)&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Number of Availability Zones (AZs)&lt;/strong&gt;: 3&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Number of public subnets&lt;/strong&gt;: 0&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Number of private subnets&lt;/strong&gt;: 3&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Private subnet CIDR block (1a, 1b, 1c)&lt;/strong&gt;: 10.1.128.0/20, 10.1.144.0/20, 10.1.160.0/20&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;NAT gateways ($)&lt;/strong&gt;: None&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;VPC endpoints&lt;/strong&gt;: S3 Gateway&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Enable DNS hostnames/resolution&lt;/strong&gt;: enabled&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Finally, create the &lt;strong&gt;integration-vpc&lt;/strong&gt;:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Resources to create&lt;/strong&gt;: VPC and more&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Name tag auto-generation&lt;/strong&gt;: integration&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;IPv4 CIDR block&lt;/strong&gt;: 10.2.0.0/16 (*)&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Number of Availability Zones (AZs)&lt;/strong&gt;: 3&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Number of public subnets&lt;/strong&gt;: 0&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Number of private subnets&lt;/strong&gt;: 3&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Private subnet CIDR block (1a, 1b, 1c)&lt;/strong&gt;: 10.2.128.0/20, 10.2.144.0/20, 10.2.160.0/20&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;NAT gateways ($)&lt;/strong&gt;: None&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;transit-gateway-and-route-table-configuration&quot;&gt;Transit Gateway and route table configuration&lt;/h1&gt;

&lt;ol&gt;
  &lt;li&gt;With VPCs created we can go ahead and create the Transit Gateway. Access the &lt;strong&gt;Transit Gateway&lt;/strong&gt; menu and click on &lt;strong&gt;Create transit gateway&lt;/strong&gt;. Give it a name and wait until the state is &lt;strong&gt;Available&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/04.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;On the screen give it a name and click on &lt;strong&gt;Create transit gateway attachments&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/05.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Wait one minute or two until you see the transit gateway state as Available:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/06.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Now we need to create &lt;strong&gt;Transit gateway attachments&lt;/strong&gt; for each VPC. Access the &lt;strong&gt;Transit gateway attachments&lt;/strong&gt; menu and click on &lt;strong&gt;Create transit gateway attachment&lt;/strong&gt; button:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/07.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Give the name egress-tga, select the transit gateway that has been just created, egress-vpc, leave all subnets enabled, and click on &lt;strong&gt;Create transit gateway attachment&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/08.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Repeat the same step for ocp-vpc and integration-vpc. Wait some minutes until all TGA state is Available.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/09.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Now access Transit gateway route tables and click on the one that has been automatically created with the TGW.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/10.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Access the Routes tab and click on Create static route button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/11.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Add the following route:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;CIDR&lt;/strong&gt;: 0.0.0.0/0&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Attachment&lt;/strong&gt;: egress-tga&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/12.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;We need to configure the egress private subnet route table to allow network packages to return back to the VPCs using the transit gateway. To do so, access one of the egress public subnets and click over the route table link:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/13.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Then click on the Routes tab and Edit routes button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/14.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Add the following routes:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;10.1.0.0/16&lt;/strong&gt; - Transit Gateway&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;10.2.0.0/16&lt;/strong&gt; - Transit Gateway&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/15.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Finally, we need to add the following rule on all subnets of ocp and integration VPCs to enable these subnets to use the Transit Gateway. To do so, click on the subnet, access the Route table tab, and click over the Route table link:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;0.0.0.0/0&lt;/strong&gt; - Transit Gateway&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/16.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Click on Edit routes:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/17.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Add the rule:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/18.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This concludes the configuration required on Transit Gateway and route tables. See next how to install Red Hat OpenShift for AWS (ROSA) on this infrastructure.&lt;/p&gt;

&lt;h1 id=&quot;deployment-of-a-red-hat-openshift-on-aws-rosa-cluster&quot;&gt;Deployment of a Red Hat OpenShift on AWS (ROSA) cluster&lt;/h1&gt;

&lt;p&gt;First, access the Red Hat Hybrid Cloud console: &lt;a href=&quot;https://console.redhat.com/openshift/create&quot;&gt;https://console.redhat.com/openshift/create&lt;/a&gt;. If you don’t have an account use the link Register for a Red Hat account to create one. Click on Create cluster button next to the Red Hat OpenShift Service on AWS (ROSA).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/19.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The first step required is to link your AWS account to your Red Hat console. To do so you will need a workstation with the AWS CLI installed and configured beforehand. Look at the references at the end of this article if you need instructions on how to install and use the AWS CLI. On the first page, click on the Associated AWS account combo box and Associate AWS account button.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/20.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Follow the instructions on the screen to download the rosa cli:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/21.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Copy the rosa login command and run it from your workstation.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rosa login &lt;span class=&quot;nt&quot;&gt;--token&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2*********&quot;&lt;/span&gt;
I: Logged &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;as &lt;span class=&quot;s1&quot;&gt;&apos;******&apos;&lt;/span&gt; on &lt;span class=&quot;s1&quot;&gt;&apos;https://api.openshift.com&apos;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rosa create ocm-role &lt;span class=&quot;nt&quot;&gt;--admin&lt;/span&gt;
I: Creating ocm role
? Role prefix: ManagedOpenShift
? Permissions boundary ARN &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;optional&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 
? Role creation mode: auto
I: Creating role using &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::********:user/*****@******-admin&apos;&lt;/span&gt;
? Create the &lt;span class=&quot;s1&quot;&gt;&apos;ManagedOpenShift-OCM-Role-11009103&apos;&lt;/span&gt; role? Yes
I: Created role &lt;span class=&quot;s1&quot;&gt;&apos;ManagedOpenShift-OCM-Role-11009103&apos;&lt;/span&gt; with ARN &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::839138491912:role/ManagedOpenShift-OCM-Role-11009103&apos;&lt;/span&gt;
I: Linking OCM role
? OCM Role ARN: arn:aws:iam::&lt;span class=&quot;k&quot;&gt;*********&lt;/span&gt;:role/ManagedOpenShift-OCM-Role-11009103
? Link the &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::*********:role/ManagedOpenShift-OCM-Role-11009103&apos;&lt;/span&gt; role with organization &lt;span class=&quot;s1&quot;&gt;&apos;**********&apos;&lt;/span&gt;? Yes
I: Successfully linked role-arn &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::*********:role/ManagedOpenShift-OCM-Role-11009103&apos;&lt;/span&gt; with organization account &lt;span class=&quot;s1&quot;&gt;&apos;**********&apos;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rosa create user-role
I: Creating User role
? Role prefix: ManagedOpenShift
? Permissions boundary ARN &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;optional&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 
? Role creation mode: auto
I: Creating ocm user role using &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::*******:user/******@*****-admin&apos;&lt;/span&gt;
? Create the &lt;span class=&quot;s1&quot;&gt;&apos;ManagedOpenShift-User-******-Role&apos;&lt;/span&gt; role? Yes
I: Created role &lt;span class=&quot;s1&quot;&gt;&apos;ManagedOpenShift-User-******-Role&apos;&lt;/span&gt; with ARN &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::*******:role/ManagedOpenShift-User-******-Role&apos;&lt;/span&gt;
I: Linking User role
? User Role ARN: arn:aws:iam::&lt;span class=&quot;k&quot;&gt;******&lt;/span&gt;:role/ManagedOpenShift-User-&lt;span class=&quot;k&quot;&gt;******&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Role&lt;/span&gt;
? Link the &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::839138491912:role/ManagedOpenShift-User-******-Role&apos;&lt;/span&gt; role with account &lt;span class=&quot;s1&quot;&gt;&apos;*****&apos;&lt;/span&gt;? Yes
I: Successfully linked role ARN &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::******:role/ManagedOpenShift-User-******-Role&apos;&lt;/span&gt; with account &lt;span class=&quot;s1&quot;&gt;&apos;*********&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After you run these commands successfully, you should now see your AWS account listed in the Associated AWS account combobox:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/22.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You will still see the following message, indicating that you need to create the account roles.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/23.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To do so, run the command as described in the message:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rosa create account-roles
I: Logged &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;as &lt;span class=&quot;s1&quot;&gt;&apos;*****&apos;&lt;/span&gt; on &lt;span class=&quot;s1&quot;&gt;&apos;https://api.openshift.com&apos;&lt;/span&gt;
I: Validating AWS credentials...
I: AWS credentials are valid!
I: Validating AWS quota...
I: AWS quota ok. If cluster installation fails, validate actual AWS resource usage against https://docs.openshift.com/rosa/rosa_getting_started/rosa-required-aws-service-quotas.html
I: Verifying whether OpenShift command-line tool is available...
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;...&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
I: Created policy with ARN &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::****:policy/ManagedOpenShift-openshift-machine-api-aws-cloud-credentials&apos;&lt;/span&gt;
I: Created policy with ARN &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::****:policy/ManagedOpenShift-openshift-cloud-credential-operator-cloud-crede&apos;&lt;/span&gt;
I: Created policy with ARN &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::****:policy/ManagedOpenShift-openshift-image-registry-installer-cloud-creden&apos;&lt;/span&gt;
I: Created policy with ARN &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::****:policy/ManagedOpenShift-openshift-ingress-operator-cloud-credentials&apos;&lt;/span&gt;
I: Created policy with ARN &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::****:policy/ManagedOpenShift-openshift-cluster-csi-drivers-ebs-cloud-credent&apos;&lt;/span&gt;
I: Created policy with ARN &lt;span class=&quot;s1&quot;&gt;&apos;arn:aws:iam::****:policy/ManagedOpenShift-openshift-cloud-network-config-controller-cloud&apos;&lt;/span&gt;
I: To create a cluster with these roles, run the following &lt;span class=&quot;nb&quot;&gt;command&lt;/span&gt;:
rosa create cluster &lt;span class=&quot;nt&quot;&gt;--sts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now click on Refresh ARNs button and we should be good to go:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/24.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On the next page fill out the Cluster name and set the Availability to Multi-zone.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/25.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Change the machine pool size if you want or leave it as-is:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/26.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Change the Cluster privacy to Private:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/27.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now copy the private subnet IDs from the ocp VCP we created before and paste here:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/28.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Set the Machine CIDR to the same range you used with the ocp VPC:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/29.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You don’t need to change the Cluster roles and policies.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/30.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Set the update strategy according to what you need:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/31.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now review the information provided and start the cluster provisioning:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/32.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You will need to wait from 40 minutes to 1 hour to have your cluster available:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/33.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When the cluster is available you will be automatically redirected to a page with the main details of your cluster.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/34.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To access our cluster we need to add an identity provider and a cluster-admin user. Click in the Access control tab, then Identity providers and select the HTPasswd:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/35.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now set the desired admin user and password:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/36.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now go back to the Access control tab and click on Add user button at Cluster Roles and Access feature.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/37.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Add the user you just created as a cluster-admin.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/38.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To check the console URL, click on the Open console button. You will not be able to access the console, as expected, as this is a private cluster.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/39.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We are going to use a jump server to access the console.&lt;/p&gt;

&lt;h1 id=&quot;publishing-dns-in-the-public-vpc&quot;&gt;Publishing DNS in the public VPC&lt;/h1&gt;

&lt;p&gt;Our cluster is private, so the DNS domain created by ROSA is. To be able to access the console and applications from the egress VPC, we should add this VPC to the DNS domain created by ROSA. To do so, access the AWS Route-53 of your AWS account, access the domain created by ROSA (it ends with openshiftapps.com), and click on the Edit hosted zone button.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/40.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now add the egress VPC in this domain:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/41.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;deployment-of-a-jump-server-to-access-the-private-subnets&quot;&gt;Deployment of a Jump server to access the private subnets&lt;/h1&gt;

&lt;p&gt;Now launch a new instance to be our jump server. You can provision any OS you prefer, such as Windows, Fedora, Red Hat, or Ubuntu that has a GUI with a supported browser version (Firefox, Chrome, or Edge). Make sure you select the egress VPC and the public subnet. Assign a public IP and use it to connect to that instance.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/42.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Connect to the instance using your preferred remote desktop tool, you should be able to access the OpenShift console from there and login using the user you defined before.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2022-08-31-rosa-transit-gateway/43.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this article we created from scratch the AWS VPCs infrastructure, connected them using a Transit Gateway and installed a Red Hat OpenShift on AWS (ROSA) in one of the private VPC. If you are interested in deploying an OpenShift cluster in a private AWS VPC, this is one of the ways to do that.&lt;/p&gt;

&lt;h1 id=&quot;references&quot;&gt;References:&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Installing or updating the latest version of the AWS CLI: &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&quot;&gt;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;AWS CLI Configuration: &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html&quot;&gt;https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 30 Aug 2022 21:05:55 +0000</pubDate>
        <link>https://giofontana.github.io/blog/2022-08-31-rosa-transit-gateway/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/2022-08-31-rosa-transit-gateway/</guid>
      </item>
    
      <item>
        <title>Windows Containers on Red Hat OpenShift: Does That Make Sense?</title>
        <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href=&quot;https://www.redhat.com/en/blog/windows-containers-on-red-hat-openshift-does-that-make-sense&quot;&gt;https://redhat.com/en/blog&lt;/a&gt; on June 28, 2021.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-windows-on-openshift&quot;&gt;Why Windows on OpenShift?&lt;/h2&gt;

&lt;p&gt;Red Hat OpenShift workers with … Windows? Perhaps it may seem strange to you; it seemed a bit strange to me at first as well. However, after seeing some numbers, I realized that this is a natural growing movement for the Containers ecosystem, in which Red Hat is one of the most active collaborators. Let’s see some numbers to understand that better. &lt;/p&gt;

&lt;p&gt;Although the growth rate of Red Hat Enterprise Linux is much greater than Windows, Windows still has a very strong presence among server operating systems in the data center, as you can see in the pie chart below, from research by IDC in 2018 (see &lt;a href=&quot;https://www.redhat.com/pt-br/blog/red-hat-leading-enterprise-linux-server-market&quot;&gt;here&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2021-06-28-windows-container-openshift/01.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This data, combined with the digital transformation movement we have been experiencing in recent years, gives us a real notion of the huge amount of effort that the market probably will demand from transformation and modernization in the next few years. Research in 2019 with more than 1,000 respondents from all regions stated that almost half of them were still in the early phase of the digital transformation:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2021-06-28-windows-container-openshift/02.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Source: 2020 Red Hat Global Customer Tech Outlook survey. Conducted by Red Hat via Qualtrics, August-September 2019. Q1. Currently, where is your company in its journey to digital transformation? n=873.&lt;/p&gt;

&lt;p&gt;This same research pointed out that “Optimizing existing IT” was still a top priority at that time. Of course, this scenario may have changed a bit after the pandemic started in 2020, but that only complicates the IT situation because t &lt;strong&gt;it is hard to innovate without getting existing IT in order first&lt;/strong&gt;. Many companies are struggling to optimize their existing legacy applications and infrastructure while, at the same time, the industry is pushing them to innovate more. If you want to see more of this report, access it &lt;a href=&quot;https://www.redhat.com/en/resources/global-tech-outlook-2020&quot;&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2021-06-28-windows-container-openshift/03.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source: 2020 Red Hat Global Customer Tech Outlook survey. Conducted by Red Hat via Qualtrics, August-September 2019.&lt;/em&gt; &lt;em&gt;Q4. Over the next 12 months, what are your company’s top IT technology funding priorities?  Please select up to 3 of the top areas your company is investing. n=674.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is when Windows on OpenShift makes real sense. A huge amount of existing IT workloads run on Windows Servers only. With this movement, &lt;strong&gt;Red Hat allows many customers to move existing Windows workloads into OpenShift with very low friction and optimize them with many benefits of the containerization,&lt;/strong&gt; as part of an Enterprise Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;Have you considered the benefits of having standard Kubernetes/OpenShift features with your existing Windows workloads? Features that are standard with OpenShift for many years now are available for Windows Server workloads also. &lt;strong&gt;Now you may have the benefits of features like self-healing, secret and configuration management, scaling, resilience, decoupled applications (from infrastructure), service discovery, and load balancing by moving the Windows workloads as containers on OpenShift.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;However, keep in mind that Windows on OpenShift is still evolving, and some features are not available or not under development yet. Here are a few of them:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Serverless&lt;/li&gt;
  &lt;li&gt;OpenShift Pipelines&lt;/li&gt;
  &lt;li&gt;Service Mesh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we have already discussed why Red Hat is making this investment and the benefits you may have with that, let’s look briefly at how to move a Windows workload to OpenShift.&lt;/p&gt;

&lt;h2 id=&quot;how-to-move-windows-workloads-to-openshift&quot;&gt;How to Move Windows Workloads to OpenShift&lt;/h2&gt;

&lt;p&gt;There is not only one strategy to move Windows workloads to OpenShift. Let’s discuss the main strategies to do that.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2021-06-28-windows-container-openshift/04.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;rehost&quot;&gt;Rehost&lt;/h2&gt;

&lt;p&gt;Red Hat OpenShift has the ability to host VMs with the &lt;strong&gt;OpenShift Virtualization&lt;/strong&gt; feature based on the KubeVirt upstream project in which you can collocate VMs with Containers in the same OpenShift cluster. This is the lowest friction (lift and shift) you have to migrate to OpenShift as you will not need to refactor the application to run as a container; however, you will not have many benefits that the containerization might bring, like a lighter footprint and decoupled applications.&lt;/p&gt;

&lt;h2 id=&quot;refactor&quot;&gt;Refactor&lt;/h2&gt;

&lt;p&gt;If the application is compatible with Windows Server 2019, you may choose to refactor it as a Windows container. As such, you will be able to have all the benefits of containerization on OpenShift, as we have covered in this article. &lt;/p&gt;

&lt;h2 id=&quot;rearchitectrebuild&quot;&gt;Rearchitect/Rebuild&lt;/h2&gt;

&lt;p&gt;With this strategy, the application will be rebuilt using .Net Core, which is Linux compatible, and you will be able to deploy it in any OpenShift cluster. You may also decide to rearchitect the application entirely using some modern cloud-native framework. In general, these applications are fragmented into micro-services that use API gateway, in-memory databases, serverless functions, and many other modern middleware layers. As such and in general, this strategy requires much more effort and is time-consuming, but it does make sense for “Mode 2” applications that require a rapid path (or IT “fast lane”) to transform business ideas into features and changes.&lt;/p&gt;

&lt;h2 id=&quot;red-hat-openshift-the-complete-set-of-workloads&quot;&gt;Red Hat OpenShift: The Complete Set of Workloads&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2021-06-28-windows-container-openshift/05.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this article, we saw why it makes sense to bring more different kinds of workloads to OpenShift. Red Hat is making this a reality by bringing together the regular Red Hat CoreOS/RHEL worker nodes, Virtual Machines with OpenShift Virtualization, and now the Windows Containers running on MS Windows Servers 2019. &lt;/p&gt;

&lt;p&gt;Ready to move your Windows workloads to Red Hat OpenShift? See &lt;a href=&quot;https://www.openshift.com/learn/topics/windows-containers&quot;&gt;here&lt;/a&gt; how to try it or talk to a Red Hatter by filling &lt;a href=&quot;https://www.redhat.com/en/contact&quot;&gt;this form&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Sun, 27 Jun 2021 21:05:55 +0000</pubDate>
        <link>https://giofontana.github.io/blog/2021-06-28-windows-container-openshift/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/2021-06-28-windows-container-openshift/</guid>
      </item>
    
      <item>
        <title>The start of the business on the multicloud journey</title>
        <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href=&quot;https://tiinside.com.br/en/14/12/2020/o-start-dos-negocios-na-jornada-multicloud/&quot;&gt;https://tiinside.com.br/&lt;/a&gt; on December 14, 2020.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Moving from one stage to another within IT is no simple step. It’s a journey, a veritable journey through the vast universe of technology, which today offers an infinite range of possibilities. Rising trends, such as hybrid cloud and multicloud, are just some of the leveraging tools for transformation and increased agility. Implementing these solutions and changing systems and behaviors to utilize them to their full potential, however, requires clarity, method, persistence, and, above all, alignment with the business.&lt;/p&gt;

&lt;p&gt;The adoption of clouds, whether public or private, for example, is already a reality in most companies. However, not all organizations are clear about their goals with this implementation, have defined the method to follow, or have managed to connect the use of these solutions with business objectives. Others still fail to persist on the journey when they begin to face the first challenges. As with any journey, it’s possible to correct routes, and knowing the path is crucial in this process.&lt;/p&gt;

&lt;h1 id=&quot;setting-a-direction&quot;&gt;Setting a direction&lt;/h1&gt;

&lt;p&gt;Every first step in life, whether personal or professional, requires clarity, and paradigm shifts in IT are no exception. It’s important to begin by understanding the business context and answering basic questions, from defining goals and efforts to achieve them to analyzing available infrastructure and investments. In other words, it’s essential to understand exactly what role IT plays as a support system within the business, viewing it comprehensively and not just as a silo.&lt;/p&gt;

&lt;p&gt;This step also involves understanding the expected results of cloud adoption. If the answer is to implement it because the entire market is using it, that’s a red flag. While beneficial to all companies, regardless of size, sector, or level of digital maturity, the cloud needs to be purposefully integrated into organizations’ IT to support day-to-day demands and improve processes.&lt;/p&gt;

&lt;p&gt;The commitment of the organization and the team must also be considered. As with any journey, results won’t be achieved immediately and will require everyone’s commitment. The team, across its various hierarchical layers, must be open to change and ready to undergo a cultural and behavioral revolution to adapt to the transformations these technologies impose. Knowledge and motivation for implementing the cloud, or any other tool, cannot be restricted to senior management. It’s crucial that they be shared with the operational team, the driving force behind (or hindering) adoption.&lt;/p&gt;

&lt;h1 id=&quot;following-routes&quot;&gt;Following routes&lt;/h1&gt;

&lt;p&gt;Once the directions have been defined and the “whys” understood, it’s time to move on to the next stage of the journey, which includes the methods and strategies that will be used to adopt the cloud in an efficient, organized, and standardized manner. While there’s no one-size-fits-all recipe, there are tips that apply to any organization. The first—and perhaps most fundamental of all—is to prioritize people. It’s people, not tools, who will truly determine the success (or failure) of cloud adoption.&lt;/p&gt;

&lt;p&gt;Another important point involves experimentation, as there is a huge range of different processes and tools involved in cloud migration. Therefore, it’s very difficult to determine which ones will be most appropriate for a given company’s needs without some level of experimentation. Adopting a process that combines experimentation and rapid feedback helps determine with greater confidence and assertiveness what should be used in each journey.&lt;/p&gt;

&lt;h1 id=&quot;opening-paths&quot;&gt;Opening paths&lt;/h1&gt;

&lt;p&gt;The journey to the cloud requires adopting several new technologies. All public clouds offer a service platform that supports application development and delivery, known as PaaS (Platform as a Service). These tools are often tempting, as they effectively increase productivity and add a series of features to application development.&lt;/p&gt;

&lt;p&gt;However, it’s important to always understand whether adopting a particular cloud provider’s tool will lock the company into that provider, known as service lock-in. To avoid this kind of surprise, it’s worth seeking out and adopting open-source solutions developed by open communities and portable to any cloud or infrastructure. Most open-source tools have procedures and automations already built in for installation on major clouds.&lt;/p&gt;

&lt;p&gt;The cloud operating model also needs to be distinct. Ideally, the cloud shouldn’t be operated in the same way a company operates in an on-premises data center. Managing a cloud environment is, or should be, different from managing a traditional virtualization environment.&lt;/p&gt;

&lt;h1 id=&quot;crossed-paths&quot;&gt;Crossed paths&lt;/h1&gt;

&lt;p&gt;Legacy infrastructure can (and should!) be integrated with cloud infrastructure, but without losing its autonomy. Companies often want to use the cloud as an overflow for their on-premises data centers. And that’s perfectly fine. However, care must be taken not to create couplings between on-premises and cloud services, creating dependencies between them, which could lead to scalability and availability issues.&lt;/p&gt;

&lt;p&gt;The journey also requires the creation of clear criteria for migrating applications to the cloud. Several strategies are available, such as lift and shift, modernization, rebuild, or even switching to a ready-made SaaS service. Each application needs to be analyzed to find the appropriate migration path. A one-size-fits-all approach won’t be efficient.&lt;/p&gt;

&lt;h1 id=&quot;continuous-cycle&quot;&gt;Continuous cycle&lt;/h1&gt;

&lt;p&gt;The journey toward IT transformation has a starting point, an imagined destination (a clearly defined objective), but never an end point. It’s a continuous cycle, a journey that allows for changes of direction, changes of reference, adaptations, and improvements. The important thing is to enjoy the beauty of the lessons learned along the way, the exchange of experiences, the contexts, and, above all, the opportunity to meet and value people.&lt;/p&gt;

&lt;p&gt;Technologies are important, and evolution is essential. But when starting a business in the multicloud era and ensuring the sustainable continuity of any organization’s projects, people are and always will be the most important asset to consider.&lt;/p&gt;
</description>
        <pubDate>Sun, 13 Dec 2020 21:05:55 +0000</pubDate>
        <link>https://giofontana.github.io/blog/2020-12-14-multicloud-journey/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/2020-12-14-multicloud-journey/</guid>
      </item>
    
      <item>
        <title>GitOps Using Red Hat OpenShift Pipelines (Tekton) and Red Hat Advanced Cluster Management</title>
        <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href=&quot;https://www.redhat.com/en/blog/gitops-using-red-hat-openshift-pipelines-tekton-and-red-hat-advanced-cluster-management-to-deploy-on-multiple-clusters&quot;&gt;https://redhat.com/en/blog&lt;/a&gt; on October 10, 2020.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Therefore, our &lt;strong&gt;Non-Production&lt;/strong&gt; cluster will have the Dev and QA projects:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2020-09-04-gitops-pipelines-acm-2/01.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On other hand, in our &lt;strong&gt;Production&lt;/strong&gt; and &lt;strong&gt;DR&lt;/strong&gt; clusters we are going to have exactly the same workload, which is the application production version - in this step we will see how to use the PlacementRule to deploy an application on multiple clusters at once!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2020-09-04-gitops-pipelines-acm-2/02.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the last article we explored the different RH ACM object types: &lt;strong&gt;Application, Channel, PlacementRule and Subscription&lt;/strong&gt;. We also deployed all &lt;strong&gt;three stages on a single cluster&lt;/strong&gt;, therefore the PlacementRule is equal for all of them. At this time we will change them to be able to deploy the application on different clusters. Let’s check them:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PlacementRule for Dev and QA:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;---
apiVersion: apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
  name: nonprod-cluster
  namespace: etherpad-acm-dev
spec:
  clusterConditions:
    - type: ManagedClusterConditionAvailable
      status: &quot;True&quot;
  clusterSelector:
    matchLabels:
      environment: nonprod
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;PlacementRule for Prod and DR:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;---
apiVersion: apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
  name: prd-and-dr-cluster
  namespace: etherpad-acm-prd
spec:
  clusterConditions:
    - type: ManagedClusterConditionAvailable
      status: &quot;True&quot;
  clusterSelector:
    matchExpressions:
    - key: environment
      operator: In
      values:
      - prod
      - dr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;NOTE: My clusters have been tagged with the following labels during their import to RH ACM:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Non Production Cluster: environment=nonprod&lt;/li&gt;
  &lt;li&gt;Production Cluster: environment=prod&lt;/li&gt;
  &lt;li&gt;DR Cluster: environment=dr&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s run it? The yaml files are in this github repo: &lt;a href=&quot;https://github.com/giofontana/rhacm-pipelines/tree/master/multipleclusters-multiple-envs&quot;&gt;https://github.com/giofontana/rhacm-pipelines/tree/master/multipleclusters-multiple-envs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2020-09-04-gitops-pipelines-acm-2/03.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note that when you create the subscription for prod environment, RH ACM starts the deployment of the etherpad application on two clusters at the same time (Prod and DR).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2020-09-04-gitops-pipelines-acm-2/04.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Therefore, we already addressed how to deploy an application on multiple namespaces in multiple clusters using RH ACM. Now we need to inject what we already have on a Tekton pipeline.&lt;/p&gt;

&lt;p&gt;Our pipeline now will cover the following process:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2020-09-04-gitops-pipelines-acm-2/05.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;[TEKTON - TASK creating-namespaces]&lt;/strong&gt; Create the namespaces on the OpenShift Hub Cluster (where RH ACM is deployed);&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;[TEKTON - TASK etherpad-dev-deployment]&lt;/strong&gt; Create Application, Channel, PlacementRule for NON-PRODUCTION and Subscription;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;[RH ACM - DEV DEPLOYMENT]&lt;/strong&gt; Deploy etherpad application on etherpad-dev namespace in NON-PRODUCTION cluster&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;[TEKTON - TASK etherpad-qa-deployment]&lt;/strong&gt; Create Application, Channel, PlacementRule for NON-PRODUCTION and Subscription;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;[RH ACM - QA DEPLOYMENT]&lt;/strong&gt; Deploy etherpad application on etherpad-qa namespace in NON-PRODUCTION cluster&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;[TEKTON - TASK etherpad-prd-deployment]&lt;/strong&gt; Create Application, Channel, PlacementRule for PRODUCTION + DR and Subscription;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;[RH ACM - PROD + DR DEPLOYMENT]&lt;/strong&gt; Deploy etherpad application on etherpad-prod namespace in PRODUCTION and DR cluster.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here we can notice something that in my perspective is really valuable about using &lt;strong&gt;RH ACM&lt;/strong&gt; as your deployment tool: You don’t need to change anything in your pipeline because now you need to deploy your application in more clusters - &lt;strong&gt;you just need to change the PlacementRule object&lt;/strong&gt; of the environment you need to change. In the last article we deployed the Dev, QA and Prod application in a single cluster, we are now deploying this same application using three different clusters and what have we changed? Only the PlacementRule object! Very nice right?&lt;/p&gt;

&lt;p&gt;To finish our article, let’s see our pipeline in action!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2020-09-04-gitops-pipelines-acm-2/06.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this article, we improved the application deployment to simulate a more real world scenario using RH ACM and Tekton: Deploy of Dev and QA version of an application in a NON-PRODUCTION cluster and deploy of the Production version in two different clusters (PROD and DR) at the same time. We also saw that, by using RH ACM, we “decouple” the deployment logic from the pipeline and we were able to change the deployment targets by only changing the PlacementRule objects and no changes were needed on the Tekton side.&lt;/p&gt;

&lt;p&gt;What did you think? Interesting, right? If you are interested in knowing more about OpenShift Pipelines and Red Hat Advanced Cluster Management for Kubernetes talk to a Red Hatter by filling &lt;a href=&quot;https://www.redhat.com/en/technologies/management/advanced-cluster-management#contact-form&quot;&gt;this form&lt;/a&gt; or talking to your Red Hat representative. You can also see more about it on the &lt;a href=&quot;https://www.youtube.com/watch?v=gKw-bJGYTQw&quot;&gt;Red Hat Videos YouTube channel&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Sun, 25 Oct 2020 21:05:55 +0000</pubDate>
        <link>https://giofontana.github.io/blog/2020-10-26-gitops-pipelines-acm-2/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/2020-10-26-gitops-pipelines-acm-2/</guid>
      </item>
    
      <item>
        <title>GitOps Using Red Hat OpenShift Pipelines (Tekton) and Red Hat Advanced Cluster Management</title>
        <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href=&quot;https://www.redhat.com/en/blog/gitops-using-red-hat-openshift-pipelines-tekton-and-red-hat-advanced-cluster-management&quot;&gt;https://redhat.com/en/blog&lt;/a&gt; on September 4, 2020.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;use-cases&quot;&gt;Use Cases:&lt;/h2&gt;

&lt;p&gt;In general, each organization defines its application life cycle strategy differently based on its own business requirements. Having that in mind, I will try to reproduce some general use cases, from simpler ones to more complex use cases.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;One single cluster:&lt;/strong&gt; multiple environments using different namespaces.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Two clusters:&lt;/strong&gt; one for non-production workloads and another one for production.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Three or more clusters:&lt;/strong&gt; one for non-production workloads and two or more clusters for production workloads (for example, Production and DR cluster).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Also, first we will simulate a simpler scenario that focuses only on the deployment of an image (essentially the CD stage) and finally a more complete pipeline including the building phase (entire CI/CD workflow).&lt;/p&gt;

&lt;p&gt;In this article, I am going to show only the first use case. Use cases 2 and 3 will be covered in the next posts.&lt;/p&gt;

&lt;h2 id=&quot;preparation&quot;&gt;Preparation:&lt;/h2&gt;

&lt;p&gt;I will not cover the installation of RH ACM and OpenShift Pipelines. The deployment is very straightforward using Operators, and you can find the instructions in the official documentation:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;table&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;td&gt;[Installing OpenShift Pipelines&lt;/td&gt;
          &lt;td&gt;Pipelines&lt;/td&gt;
          &lt;td&gt;OpenShift Container Platform 4.5](https://docs.openshift.com/container-platform/4.5/pipelines/installing-pipelines.html)&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_advanced_cluster_management_for_kubernetes/2.0/html/install/index&quot;&gt;Install Red Hat Advanced Cluster Management for Kubernetes 2.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both tools have been installed in a dedicated OpenShift cluster, which RH ACM names as “hub cluster”. Also, the managed-cluster has been imported in RH ACM with name “&lt;strong&gt;prd&lt;/strong&gt;”:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2020-09-04-gitops-pipelines-acm/01.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;gitops-in-a-single-openshift-cluster-using-red-hat-openshift-pipelines-tekton-and-red-hat-advanced-cluster-management&quot;&gt;GitOps in a Single OpenShift Cluster Using Red Hat OpenShift Pipelines (Tekton) and Red Hat Advanced Cluster Management&lt;/h2&gt;

&lt;p&gt;In this use case, I will be using the &lt;strong&gt;etherpad&lt;/strong&gt;, a sample application from &lt;a href=&quot;https://github.com/redhat-gpte-devopsautomation/rhacm-labs&quot;&gt;rhacm-labs&lt;/a&gt;. &lt;/p&gt;

&lt;h3 id=&quot;resources&quot;&gt;Resources:&lt;/h3&gt;

&lt;p&gt;Basically, we have three types of resources we will need to handle:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Application resources&lt;/strong&gt;: YAML files that describe the k8s application, such as the application deployment, route, services, and so on.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2020-09-04-gitops-pipelines-acm/02.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;RH ACM resources&lt;/strong&gt;: Files that will create the objects that RH ACM uses to manage an application. The following objects will be used:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Channels&lt;/strong&gt; (channel.apps.open-cluster-management.io): define the source repositories that a cluster can subscribe to with a subscription, and can be the following types: GitHub repositories, Helm release registries, object stores, and resource template (deployable) namespaces on the hub cluster. In our case, we are using a GitHub channel.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Subscriptions&lt;/strong&gt; (subscription.apps.open-cluster-management.io): allow clusters to subscribe to a source repository (channel). Subscriptions can be applied locally to the hub or to managed-clusters.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Placement rules&lt;/strong&gt; (placementrule.apps.open-cluster-management.io): define the target clusters where subscriptions deploy and maintain the Kubernetes resources. You can use placement rules to help you facilitate the multicluster deployment. Placement rules can be shared across subscriptions.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Applications&lt;/strong&gt; (application.app.k8s.io): Used in Red Hat Advanced Cluster Management for Kubernetes for grouping Kubernetes resources that make up an application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2020-09-04-gitops-pipelines-acm/03.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Tekton resources&lt;/strong&gt;: Files that will create the tasks and pipeline to deploy the application using RH ACM in three different stages: development, QA, and production.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2020-09-04-gitops-pipelines-acm/04.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;All source code used in this demo is in this git repository: &lt;a href=&quot;https://github.com/giofontana/rhacm-pipelines/tree/master/onecluster-multiple-envs&quot;&gt;https://github.com/giofontana/rhacm-pipelines/tree/master/onecluster-multiple-envs&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;deploying-an-application-using-rh-acm&quot;&gt;Deploying an application using RH ACM&lt;/h3&gt;

&lt;p&gt;To begin our process, let’s look first on how to deploy the sample application using RH ACM. The first thing we will need to do is to create namespaces on the hub cluster that will manage the application:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;---
apiVersion: v1
kind: Namespace
metadata:
  name: etherpad-acm-dev
---
apiVersion: v1
kind: Namespace
metadata:
  name: etherpad-acm-qa
---
apiVersion: v1
kind: Namespace
metadata:
  name: etherpad-acm-prd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We are creating a project for each stage of our application life cycle (DEV/QA/PRD). This is a sample; in a real-world scenario, you should probably use some annotations and labels in your namespaces also.&lt;/p&gt;

&lt;p&gt;We also will have an application, channel, placement-rule, and subscription YAML file for each stage:&lt;/p&gt;

&lt;p&gt;Example for &lt;strong&gt;dev&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;---
apiVersion: app.k8s.io/v1beta1
kind: Application
metadata:
  name: etherpad-acm-dev
  namespace: etherpad-acm-dev
spec:
  componentKinds:
  - group: apps.open-cluster-management.io
    kind: Subscription
  descriptor: {}
  selector:
    matchExpressions:
    - key: app
      operator: In
      values:
      - etherpad-acm-dev
---
apiVersion: apps.open-cluster-management.io/v1
kind: Channel
metadata:
  name: etherpad-app-latest
  namespace: etherpad-acm-dev
spec:
  type: GitHub
  pathname: https://github.com/giofontana/rhacm-pipelines.git 
---
apiVersion: apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
  name: dev-cluster
  namespace: etherpad-acm-dev
spec:
  clusterConditions:
    - type: ManagedClusterConditionAvailable
      status: &quot;True&quot;
  clusterSelector:
    matchLabels:
      environment: prd
---
apiVersion: apps.open-cluster-management.io/v1
kind: Subscription
metadata:
  name: etherpad-acm-dev
  namespace: etherpad-acm-dev
  labels:
    app: etherpad-acm-dev
  annotations:
    apps.open-cluster-management.io/github-path: onecluster-multiple-envs/app-resources/etherpad/dev
spec:
  channel: etherpad-acm-dev/etherpad-app-latest
  placement:
    placementRef:
      kind: PlacementRule
      name: dev-cluster
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note: You will notice that the clusterSelector for all stages are equal. This is because for while, we are using one single managed-cluster. You can switch it according to as many clusters as you need.&lt;/p&gt;

&lt;p&gt;This is the folder structure we will have in the end:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;onecluster-multiple-envs/rhacm
├── namespaces.yaml
├── dev
│   ├── application.yaml
│   ├── channel.yaml
│   ├── placement-rule.yaml
│   └── subscription.yaml
├── prd
│   ├── application.yaml
│   ├── channel.yaml
│   ├── placement-rule.yaml
│   └── subscription.yaml
└── qa
    ├── application.yaml
    ├── channel.yaml
    ├── placement-rule.yaml
    └── subscription.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, let’s test it. To deploy this structure, you just need to run each YAML file against the RH ACM hub cluster.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2020-09-04-gitops-pipelines-acm/05.gif&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On the right side, you will see the managed cluster. Note that as soon as the subscription is created on the hub cluster, the deployment starts on the managed cluster: first DEV, then QA, and finally PRD.&lt;/p&gt;

&lt;h3 id=&quot;creating-a-pipeline-using-rh-acm-resources&quot;&gt;Creating a Pipeline Using RH ACM Resources&lt;/h3&gt;

&lt;p&gt;Now that we already have our three projects for each stage (DEV/QA/PRD) being deployed and managed using RH ACM, we will create a pipeline to create the RH ACM resources automatically using Tekton.&lt;/p&gt;

&lt;p&gt;To do that, we have the following tasks in Tekton:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;create_namespaces: Create the namespaces needed in the hub cluster.&lt;/li&gt;
  &lt;li&gt;etherpad-dev-deployment: Deployment of DEV stage of etherpad.&lt;/li&gt;
  &lt;li&gt;etherpad-qa-deployment: Deployment of QA stage of etherpad.&lt;/li&gt;
  &lt;li&gt;etherpad-prd-deployment: Deployment of PRD stage of etherpad.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Also, we created a pipeline with the following structure:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: deploy-using-acm
spec:
  workspaces:
  - name: shared-workspace
  params:
  - name: deployment-name
    type: string
    description: name of the deployment to be patched
  - name: git-url
    type: string
    description: url of the git repo for the code of deployment
    default: &quot;https://github.com/giofontana/rhacm-pipelines.git&quot;
  - name: git-revision
    type: string
    description: revision to be used from repo of the code for deployment
    default: &quot;master&quot;
  tasks:
  - name: fetch-repository
    taskRef:
      name: git-clone
      kind: ClusterTask
    workspaces:
    - name: output
      workspace: shared-workspace
    params:
    - name: url
      value: $(params.git-url)
    - name: subdirectory
      value: &quot;&quot;
    - name: deleteExisting
      value: &quot;true&quot;
    - name: revision
      value: $(params.git-revision)
        - name: create-namespaces
    taskRef:
      kind: Task        
      name: create-namespaces
    workspaces:
    - name: source
      workspace: shared-workspace
    runAfter:
    - fetch-repository     
  - name: etherpad-dev-deployment
    taskRef:
      kind: Task        
      name: etherpad-dev-deployment
    workspaces:
    - name: source
      workspace: shared-workspace
    runAfter:
    - create-namespaces      
  - name: etherpad-qa-deployment
    taskRef:
      kind: Task        
      name: etherpad-qa-deployment
    workspaces:
    - name: source
      workspace: shared-workspace
    runAfter:
    - etherpad-dev-deployment  
  - name: etherpad-prd-deployment
    taskRef:
      kind: Task        
      name: etherpad-prd-deployment
    workspaces:
    - name: source
      workspace: shared-workspace
    runAfter:
    - etherpad-qa-deployment 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can now deploy the Tekton objects:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; oc new-project etherpad-cicd
Now using project &quot;etherpad-cicd&quot; on server &quot;https://api.acmhub.rhbr-labs.com:6443&quot;.
You can add applications to this project with the &apos;new-app&apos; command. For example, try:
    oc new-app ruby~https://github.com/sclorg/ruby-ex.git
to build a new example application in Python. Or use kubectl to deploy a simple Kubernetes application:
    kubectl create deployment hello-node --image=gcr.io/hello-minikube-zero-install/hello-node
&amp;gt;  oc get serviceaccount pipeline
NAME       SECRETS   AGE
pipeline   2         29s
&amp;gt; cd onecluster-multiple-envs/tekton
&amp;gt; oc create -f tasks/01_create_namespaces.yaml
task.tekton.dev/create-namespaces created
&amp;gt; oc create -f tasks/02_create_dev_app_using_acm.yaml
task.tekton.dev/etherpad-dev-deployment created
&amp;gt; oc apply -f tasks/03_create_qa_app_using_acm.yaml
task.tekton.dev/etherpad-qa-deployment created
&amp;gt; oc apply -f tasks/04_create_prd_app_using_acm.yaml
task.tekton.dev/etherpad-prd-deployment created
&amp;gt; oc adm policy add-cluster-role-to-user cluster-admin system:serviceaccount:etherpad-cicd:pipeline
clusterrole.rbac.authorization.k8s.io/cluster-admin added: &quot;system:serviceaccount:etherpad-cicd:pipeline&quot;
&amp;gt; oc apply -f pipeline-acm.yaml
pipeline.tekton.dev/deploy-using-acm created
&amp;gt; oc apply -f prepare/tekton-source-pvc.yaml
persistentvolumeclaim/source-pvc created
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And run our pipeline:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; oc apply -f pipelinerun.yaml
pipelinerun.tekton.dev/deploy-using-acm-run-1 created
&amp;gt; tkn pipelinerun logs deploy-using-acm-run-1 -f
[fetch-repository : clone] + CHECKOUT_DIR=/workspace/output/
[fetch-repository : clone] + &apos;[[&apos; true &apos;==&apos; true ]]
[fetch-repository : clone] + cleandir
[fetch-repository : clone] + &apos;[[&apos; -d /workspace/output/ ]]
[fetch-repository : clone] + rm -rf /workspace/output//onecluster-multiple-envs
[fetch-repository : clone] + rm -rf /workspace/output//.git
[fetch-repository : clone] + rm -rf &apos;/workspace/output//..?*&apos;
[fetch-repository : clone] + test -z 
[fetch-repository : clone] + test -z 
[fetch-repository : clone] + test -z 
[fetch-repository : clone] + /ko-app/git-init -url https://github.com/giofontana/rhacm-pipelines.git -revision master -refspec  -path /workspace/output/ &apos;-sslVerify=true&apos; &apos;-submodules=true&apos; -depth 1
[fetch-repository : clone] {&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:1597794549.2025642,&quot;caller&quot;:&quot;git/git.go:136&quot;,&quot;msg&quot;:&quot;Successfully cloned https://github.com/giofontana/rhacm-pipelines.git @ f111d89c17e5647d3cd4dedca6968f20b73cfb0d (grafted, HEAD, origin/master) in path /workspace/output/&quot;}
[fetch-repository : clone] {&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:1597794549.240191,&quot;caller&quot;:&quot;git/git.go:177&quot;,&quot;msg&quot;:&quot;Successfully initialized and updated submodules in path /workspace/output/&quot;}
[fetch-repository : clone] + cd /workspace/output/
[fetch-repository : clone] + git rev-parse HEAD
[fetch-repository : clone] + tr -d &apos;\n&apos;
[fetch-repository : clone] + RESULT_SHA=f111d89c17e5647d3cd4dedca6968f20b73cfb0d
[fetch-repository : clone] + EXIT_CODE=0
[fetch-repository : clone] + &apos;[&apos; 0 &apos;!=&apos; 0 ]
[fetch-repository : clone] + echo -n f111d89c17e5647d3cd4dedca6968f20b73cfb0d
[create-namespaces : creating-namespaces] **** Creating namespaces ****
[create-namespaces : creating-namespaces] namespace/etherpad-acm-dev unchanged
[create-namespaces : creating-namespaces] namespace/etherpad-acm-qa unchanged
[create-namespaces : creating-namespaces] namespace/etherpad-acm-prd unchanged
[etherpad-dev-deployment : etherpad-dev-deployment] **** DEV ENVIRONMENT: Creating application on RH ACM ****
[etherpad-dev-deployment : etherpad-dev-deployment] application.app.k8s.io/etherpad-acm-dev unchanged
[etherpad-dev-deployment : etherpad-dev-deployment] **** DEV ENVIRONMENT: Creating channel on RH ACM ****
[etherpad-dev-deployment : etherpad-dev-deployment] channel.apps.open-cluster-management.io/etherpad-app-latest unchanged
[etherpad-dev-deployment : etherpad-dev-deployment] **** DEV ENVIRONMENT: Creating placementrule on RH ACM ****
[etherpad-dev-deployment : etherpad-dev-deployment] placementrule.apps.open-cluster-management.io/dev-cluster unchanged
[etherpad-dev-deployment : etherpad-dev-deployment] **** DEV ENVIRONMENT: Creating subscription on RH ACM ****
[etherpad-dev-deployment : etherpad-dev-deployment] subscription.apps.open-cluster-management.io/etherpad-acm-dev unchanged
[etherpad-qa-deployment : etherpad-qa-deployment] **** QA ENVIRONMENT: Creating application on RH ACM ****
[etherpad-qa-deployment : etherpad-qa-deployment] application.app.k8s.io/etherpad-acm-qa unchanged
[etherpad-qa-deployment : etherpad-qa-deployment] **** QA ENVIRONMENT: Creating channel on RH ACM ****
[etherpad-qa-deployment : etherpad-qa-deployment] channel.apps.open-cluster-management.io/etherpad-app-latest unchanged
[etherpad-qa-deployment : etherpad-qa-deployment] **** QA ENVIRONMENT: Creating placementrule on RH ACM ****
[etherpad-qa-deployment : etherpad-qa-deployment] placementrule.apps.open-cluster-management.io/qa-cluster unchanged
[etherpad-qa-deployment : etherpad-qa-deployment] **** QA ENVIRONMENT: Creating subscription on RH ACM ****
[etherpad-qa-deployment : etherpad-qa-deployment] subscription.apps.open-cluster-management.io/etherpad-acm-qa unchanged
[etherpad-prd-deployment : etherpad-qa-deployment] **** PRD ENVIRONMENT: Creating application on RH ACM ****
[etherpad-prd-deployment : etherpad-qa-deployment] application.app.k8s.io/etherpad-acm-prd unchanged
[etherpad-prd-deployment : etherpad-qa-deployment] **** PRD ENVIRONMENT: Creating channel on RH ACM ****
[etherpad-prd-deployment : etherpad-qa-deployment] channel.apps.open-cluster-management.io/etherpad-app-latest unchanged
[etherpad-prd-deployment : etherpad-qa-deployment] **** PRD ENVIRONMENT: Creating placementrule on RH ACM ****
[etherpad-prd-deployment : etherpad-qa-deployment] placementrule.apps.open-cluster-management.io/prd-cluster unchanged
[etherpad-prd-deployment : etherpad-qa-deployment] **** PRD ENVIRONMENT: Creating subscription on RH ACM ****
[etherpad-prd-deployment : etherpad-qa-deployment] subscription.apps.open-cluster-management.io/etherpad-acm-prd unchanged
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that the tasks have “unchanged” results because we already have our application deployed.&lt;/p&gt;

&lt;p&gt;Here is how we see this pipeline in the web console:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2020-09-04-gitops-pipelines-acm/06.png&quot; alt=&quot;Screenshot&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;In this article, we saw how to create three applications on RH ACM in your hub cluster to deploy a sample application for each stage of its life cycle: development, QA, and production. We also learned how to create a pipeline using Tekton to make those deployments automatically. &lt;/p&gt;

&lt;p&gt;In the next article, we are going to explore how to have a similar case, but deploying the application in more than one cluster (use cases 2 and 3). In a final article, I am planning to share an entire CI/CD pipeline, including the build phase that we are not mentioning yet.&lt;/p&gt;

&lt;p&gt;I encourage you to try out Red Hat Advanced Cluster Management for Kubernetes if you are looking for a solution for multicluster; see &lt;a href=&quot;https://www.redhat.com/en/technologies/management/advanced-cluster-management&quot;&gt;here&lt;/a&gt; how to try it. Combining it with Red Hat OpenShift Pipelines (Tekton) will give you a powerful native Kubernetes solution for GitOps. You can find more information about it in the &lt;a href=&quot;https://docs.openshift.com/container-platform/4.5/pipelines/understanding-openshift-pipelines.html&quot;&gt;OpenShift documentation page&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Thu, 03 Sep 2020 21:05:55 +0000</pubDate>
        <link>https://giofontana.github.io/blog/2020-09-04-gitops-pipelines-acm/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/2020-09-04-gitops-pipelines-acm/</guid>
      </item>
    
      <item>
        <title>How to Restore All Masters in OpenShift 4.x</title>
        <description>&lt;p&gt;In this article I am going to show how to restore all masters servers, in case you need to restore your cluster of masters and etcds.&lt;/p&gt;

&lt;p&gt;This procedure was created with my friend &lt;a href=&quot;https://github.com/pecorawal&quot;&gt;Rafael Pécora&lt;/a&gt; and it is based on &lt;a href=&quot;https://docs.openshift.com/container-platform/4.3/backup_and_restore/disaster_recovery/scenario-1-infra-recovery.html&quot;&gt;OpenShift documentation&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;NOTE: This procedure has been tested on OpenShift 4.3 and it does not apply to version 4.4 and above.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To do so, you will need to have at least one functional master/etcd server still working or at least you should have a recent backup to recover one of the masters and start the recovery process.&lt;/p&gt;

&lt;p&gt;Therefore we should run the following tasks:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;ETCD Backup&lt;/strong&gt;: Backup one of the remaining funcional master nodes. In our case this will be the server master-0 (etcd-0).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Restore backup and etcd cluster&lt;/strong&gt;: Restore backup selecting one etcd member only in the cluster (in our case will be the master-0/etcd-0)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Remove 2 masters nodes&lt;/strong&gt;: Remove other 2 masters nodes (master-1/etcd-1 and master-2/etcd-2)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Recreate masters&lt;/strong&gt;: Recreate both machines (master-1 and master-2)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Update DNS and LB records&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Run etcd tokenize&lt;/strong&gt; on the remaining funcional master node (master-0/etcd-0)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Run etcd-member-recover for new members&lt;/strong&gt; (master-1/etcd-1 and master-2/etcd-2)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now you already have your cluster of masters entirely functional and recovered. Only to exercise this process a little bit more, we will also replace the first master node (master-0/etcd-0). In order to do that, we will add the following tasks to our procedure:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Remove the etcd member etcd-0&lt;/strong&gt; (master-0/etcd-0)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Remove server master-0&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Create a new server master-0&lt;/strong&gt; (master-0/etcd-0)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Update DNS and LB records&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Run etcd tokenize&lt;/strong&gt; on one of remaining funcional master node (now can be either master-1/etcd-1 or master-2/etcd-2)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Run etcd-member-recover for new the member&lt;/strong&gt; (master-0/etcd-0)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;etcd-backup&quot;&gt;ETCD Backup&lt;/h2&gt;

&lt;p&gt;The first step we should follow is to have a funcional backup of the cluster etcd. If you lost your entire cluster, you should restore it from an existing etcd backup that you should already have (in this case you already have your backup and you can skip this step). This process is documented in the OpenShift official process.footnote:[https://docs.openshift.com/container-platform/4.3/backup_and_restore/backing-up-etcd.html]&lt;/p&gt;

&lt;p&gt;In our case, we elect the &lt;em&gt;master-0&lt;/em&gt; as the remaining funcional node.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Procedure:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;SSH to master-0
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/id_rsa core@10.0.140.240
&lt;span class=&quot;go&quot;&gt;Red Hat Enterprise Linux CoreOS 43.81.202005180953.0
  Part of OpenShift 4.3, RHCOS is a Kubernetes native operating system
  managed by the Machine Config Operator (`clusteroperator/machine-config`).

&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;WARNING: Direct SSH access to machines is not recommended;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;instead,
&lt;span class=&quot;go&quot;&gt;make configuration changes via `machineconfig` objects:
  https://docs.openshift.com/container-platform/4.3/architecture/architecture-rhcos.html

Last login: Fri May 22 10:32:14 2020 from 172.31.36.49
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-140-240 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;[root@ip-10-0-140-240 ~]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;/usr/local/bin/etcd-snapshot-backup.sh ./assets/backup
&lt;span class=&quot;go&quot;&gt;Creating asset directory ./assets
683bef0adcff2cce1214a3cd00b78a06fedf6866f02d020cedc8ca89bcd902d3
etcdctl version: 3.3.17
API version: 3.3
Trying to backup etcd client certs..
etcd client certs found in /etc/kubernetes/static-pod-resources/kube-apiserver-pod-6 backing up to ./assets/backup/
Backing up /etc/kubernetes/manifests/etcd-member.yaml to ./assets/backup/
Trying to backup latest static pod resources..
{&quot;level&quot;:&quot;warn&quot;,&quot;ts&quot;:&quot;2020-05-22T10:35:19.130Z&quot;,&quot;caller&quot;:&quot;clientv3/retry_interceptor.go:116&quot;,&quot;msg&quot;:&quot;retry stream intercept&quot;}
Snapshot saved at ./assets/backup/snapshot_2020-05-22_103518.db
snapshot db and kube resources are successfully saved to ./assets/backup!

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h1 id=&quot;copy-backup-to-a-bastion-host&quot;&gt;Copy backup to a bastion host&lt;/h1&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ip-10-0-140-240 ~]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;tar&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-cvzf&lt;/span&gt; etcd-20200522-bkp.tgz assets 
&lt;span class=&quot;go&quot;&gt;assets/
assets/backup/
assets/backup/etcd-ca-bundle.crt
assets/backup/etcd-client.crt
assets/backup/etcd-client.key
... omitted ...
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[root@ip-10-0-140-240 ~]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mv &lt;/span&gt;etcd-20200522-bkp.tgz /home/core/
&lt;span class=&quot;gp&quot;&gt;[root@ip-10-0-140-240 ~]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chown &lt;/span&gt;core:core /home/core/etcd-20200522-bkp.tgz 
&lt;span class=&quot;gp&quot;&gt;[root@ip-10-0-140-240 ~]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;logout
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-140-240 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;logout
Connection to 10.0.140.240 closed.

&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;scp &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/id_rsa core@10.0.140.240:/home/core/etcd-20200522-bkp.tgz &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;etcd-20200522-bkp.tgz                                                                                                                   100%   23MB  83.0MB/s   00:00    

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h1 id=&quot;restore-backup-and-etcd-cluster&quot;&gt;Restore backup and etcd cluster&lt;/h1&gt;

&lt;p&gt;Now we should restore the backup selecting etcd-0 as the only etcd member in the cluster. We should do this to restore the etcd cluster quorum and, as such, restoring access to our OpenShift api and console.&lt;/p&gt;

&lt;p&gt;This procedure should be done in our remaining funcional node or the first member selected to be recovered. In our case this is the server master-0/etcd-0.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Procedure:&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Copy the backup to our remaining funcional node &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;or the first member selected to be recovered&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;scp &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/id_rsa etcd-20200522-bkp.tgz core@10.0.140.240:/home/core/
&lt;span class=&quot;go&quot;&gt;etcd-20200522-bkp.tgz                                                                                                                   100%   23MB 118.8MB/s   00:00    

&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;SSH to master-0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;our remaining funcional node&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-140-240 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;tar&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-xvzf&lt;/span&gt;  etcd-20200522-bkp.tgz
&lt;span class=&quot;go&quot;&gt;assets/
assets/backup/
assets/backup/etcd-ca-bundle.crt
assets/backup/etcd-client.crt
assets/backup/etcd-client.key
... omitted ...
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-140-240 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;INITIAL_CLUSTER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;etcd-member-ip-10-0-140-240.ec2.internal=https://etcd-0.ocp.rhbr-labs.com:2380&quot;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-140-240 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; /usr/local/bin/etcd-snapshot-restore.sh /home/core/assets/backup &lt;span class=&quot;nv&quot;&gt;$INITIAL_CLUSTER&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;e72fde15c357a44bd242d28853e8ad66fba2e0ce472c4602f55b9634c8dadf46
etcdctl version: 3.3.17
API version: 3.3
etcd-member.yaml found in ./assets/backup/
Stopping all static pods..
..stopping etcd-member.yaml
..stopping kube-scheduler-pod.yaml
..stopping kube-controller-manager-pod.yaml
..stopping kube-apiserver-pod.yaml
... omitted ...
Restoring etcd member etcd-member-ip-10-0-140-240.ec2.internal from snapshot..
2020-05-22 10:47:08.134229 I | pkg/netutil: resolving etcd-0.ocp.rhbr-labs.com:2380 to 10.0.140.240:2380
2020-05-22 10:47:09.775887 I | mvcc: restore compact to 25232
2020-05-22 10:47:09.814652 I | etcdserver/membership: added member 890a07c73df999b0 [https://etcd-0.ocp.rhbr-labs.com:2380] to cluster ea5a775da961a326
Starting static pods..
..starting etcd-member.yaml
..starting kube-scheduler-pod.yaml
..starting kube-controller-manager-pod.yaml
..starting kube-apiserver-pod.yaml
Starting kubelet..
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Some minutes after above procedure you should have your cluster functional again. After that, check if we only have etcd-0 in our etcd cluster:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-etcd rsh &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;oc get pods &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-etcd | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;ip-10-0-140-240 | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# &amp;lt;1&amp;gt;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Defaulting container name to etcd-member.
Use &apos;oc describe pod/etcd-member-ip-10-0-140-240.ec2.internal -n openshift-etcd&apos; to see all of the containers in this pod.
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;sh-4.2#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCDCTL_API&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3 &lt;span class=&quot;nv&quot;&gt;ETCDCTL_CACERT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/ssl/etcd/ca.crt &lt;span class=&quot;nv&quot;&gt;ETCDCTL_CERT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;find /etc/ssl/ &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;peer&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;crt&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ETCDCTL_KEY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;find /etc/ssl/ &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;peer&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;key&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;sh-4.2#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;etcdctl member list &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; table
&lt;span class=&quot;go&quot;&gt;+------------------+---------+------------------------------------------+---------------------------------------+---------------------------+
|        ID        | STATUS  |                   NAME                   |              PEER ADDRS               |       CLIENT ADDRS        |
+------------------+---------+------------------------------------------+---------------------------------------+---------------------------+
| 890a07c73df999b0 | started | etcd-member-ip-10-0-140-240.ec2.internal | https://etcd-0.ocp.rhbr-labs.com:2380 | https://10.0.140.240:2379 |
+------------------+---------+------------------------------------------+---------------------------------------+---------------------------+
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;sh-4.2#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&amp;lt;1&amp;gt; Change ip-10-0-140-240 for the ip of your master-0&lt;/p&gt;

&lt;h1 id=&quot;remove-2-masters-nodes&quot;&gt;Remove 2 masters nodes&lt;/h1&gt;

&lt;p&gt;(master-1/etcd-1 and master-2/etcd-2)&lt;/p&gt;

&lt;p&gt;Now we should remove problematic master nodes to replace them further. Before deleting them let’s export their machine configurations to be used to recreate them.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Procedure:&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Export master-0
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get machine &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;master-0 | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{ print $1 }&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; yaml &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; new-master-0.yaml
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Export master-1
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get machine &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;master-1 | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{ print $1 }&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; yaml &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; new-master-1.yaml
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Export master-2
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get machine &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;master-2 | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{ print $1 }&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; yaml &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; new-master-2.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we can remove masters 1 and 2.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Procedure:&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Remove master-1
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc delete machine &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;master-1 | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{ print $1 }&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;machine.machine.openshift.io &quot;ocp-w2lhz-master-1&quot; deleted
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Remove master-2
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc delete machine &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;master-2 | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{ print $1 }&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;machine.machine.openshift.io &quot;ocp-w2lhz-master-2&quot; deleted
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this time, your cluster may become nonfunctional again:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api 
&lt;span class=&quot;go&quot;&gt;The connection to the server api.ocp.rhbr-labs.com:6443 was refused - did you specify the right host or port?
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you get this problem you can remove the servers manually on AWS and restore the backup again in the master-0 (step above).&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-140-240 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;INITIAL_CLUSTER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;etcd-member-ip-10-0-140-240.ec2.internal=https://etcd-0.ocp.rhbr-labs.com:2380&quot;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-140-240 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; /usr/local/bin/etcd-snapshot-restore.sh /home/core/assets/backup &lt;span class=&quot;nv&quot;&gt;$INITIAL_CLUSTER&lt;/span&gt;                                                      
&lt;span class=&quot;go&quot;&gt;b3dd01ff7158ee8809b761d97be626d1e42760f2de505fcc4e950a951d213f1b
etcdctl version: 3.3.17
... omitted ...
2020-05-22 11:12:16.784231 I | etcdserver/membership: added member 890a07c73df999b0 [https://etcd-0.ocp.rhbr-labs.com:2380] to cluster ea5a775da961a326
Starting static pods..
..starting etcd-member.yaml
..starting kube-scheduler-pod.yaml
..starting kube-controller-manager-pod.yaml
..starting kube-apiserver-pod.yaml
Starting kubelet..
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After some minutes that you recovered your backup you will see that the api will become functional again, but still showing master-1 and master-2 (because at the time of your backup both servers still exists). Wait up to 10 minutes and the OpenShift will move those masters to “Failed” state:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api 
&lt;span class=&quot;go&quot;&gt;NAME                                PHASE     TYPE        REGION      ZONE         AGE
ocp-w2lhz-master-0                  Running   m4.xlarge   us-east-1   us-east-1a   94m
ocp-w2lhz-master-1                  Failed    m4.xlarge   us-east-1   us-east-1b   94m
ocp-w2lhz-master-2                  Failed    m4.xlarge   us-east-1   us-east-1c   94m
ocp-w2lhz-worker-us-east-1a-nq6g2   Running   m4.large    us-east-1   us-east-1a   89m
ocp-w2lhz-worker-us-east-1b-chzl6   Running   m4.large    us-east-1   us-east-1b   89m
ocp-w2lhz-worker-us-east-1c-6zdpt   Running   m4.large    us-east-1   us-east-1c   89m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now remove the machines again:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc delete machine &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;master-1 | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{ print $1 }&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;machine.machine.openshift.io &quot;ocp-w2lhz-master-1&quot; deleted
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc delete machine &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;master-2 | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{ print $1 }&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;machine.machine.openshift.io &quot;ocp-w2lhz-master-2&quot; deleted
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api
&lt;span class=&quot;go&quot;&gt;NAME                                PHASE     TYPE        REGION      ZONE         AGE
ocp-w2lhz-master-0                  Running   m4.xlarge   us-east-1   us-east-1a   94m
ocp-w2lhz-worker-us-east-1a-nq6g2   Running   m4.large    us-east-1   us-east-1a   90m
ocp-w2lhz-worker-us-east-1b-chzl6   Running   m4.large    us-east-1   us-east-1b   90m
ocp-w2lhz-worker-us-east-1c-6zdpt   Running   m4.large    us-east-1   us-east-1c   90m
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get nodes
&lt;span class=&quot;go&quot;&gt;NAME                           STATUS   ROLES    AGE   VERSION
ip-10-0-129-242.ec2.internal   Ready    worker   86m   v1.16.2
ip-10-0-140-240.ec2.internal   Ready    master   94m   v1.16.2
ip-10-0-148-0.ec2.internal     Ready    worker   86m   v1.16.2
ip-10-0-161-99.ec2.internal    Ready    worker   86m   v1.16.2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;recreate-masters&quot;&gt;Recreate masters&lt;/h1&gt;

&lt;p&gt;(master-1 and master-2)&lt;/p&gt;

&lt;p&gt;Now we will use the machine config yaml files that you exported before.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Procedure:&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;vi new-master-1.yaml
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Remove entire &lt;span class=&quot;s2&quot;&gt;&quot;status&quot;&lt;/span&gt; section
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Remove the providerID field
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;vi new-master-2.yaml
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Remove entire &lt;span class=&quot;s2&quot;&gt;&quot;status&quot;&lt;/span&gt; section
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Remove the providerID field
&lt;span class=&quot;go&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Now recreate the masters
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc create &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; new-master-1.yaml
&lt;span class=&quot;go&quot;&gt;machine.machine.openshift.io/ocp-w2lhz-master-1 created
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc create &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; new-master-2.yaml
&lt;span class=&quot;go&quot;&gt;machine.machine.openshift.io/ocp-w2lhz-master-2 created
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion install]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api
&lt;span class=&quot;go&quot;&gt;NAME                                PHASE          TYPE        REGION      ZONE         AGE
ocp-w2lhz-master-0                  Running        m4.xlarge   us-east-1   us-east-1a   96m
ocp-w2lhz-master-1                  Provisioning   m4.xlarge   us-east-1   us-east-1a   13s
ocp-w2lhz-master-2                  Provisioning   m4.xlarge   us-east-1   us-east-1a   8s
ocp-w2lhz-worker-us-east-1a-nq6g2   Running        m4.large    us-east-1   us-east-1a   91m
ocp-w2lhz-worker-us-east-1b-chzl6   Running        m4.large    us-east-1   us-east-1b   91m
ocp-w2lhz-worker-us-east-1c-6zdpt   Running        m4.large    us-east-1   us-east-1c   91m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Wait until the new masters (master-1 and master-2) to be in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Running&lt;/code&gt; state:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api
&lt;span class=&quot;go&quot;&gt;NAME                                PHASE     TYPE        REGION      ZONE         AGE
ocp-w2lhz-master-0                  Running   m4.xlarge   us-east-1   us-east-1a   118m
ocp-w2lhz-master-1                  Running   m4.xlarge   us-east-1   us-east-1a   22m
ocp-w2lhz-master-2                  Running   m4.xlarge   us-east-1   us-east-1a   22m
ocp-w2lhz-worker-us-east-1a-nq6g2   Running   m4.large    us-east-1   us-east-1a   114m
ocp-w2lhz-worker-us-east-1b-chzl6   Running   m4.large    us-east-1   us-east-1b   114m
ocp-w2lhz-worker-us-east-1c-6zdpt   Running   m4.large    us-east-1   us-east-1c   114m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;update-dns-and-lb-records&quot;&gt;Update DNS and LB records&lt;/h1&gt;

&lt;p&gt;Now we already have our 3 masters online again, however we still have only one etcd running on the cluster (on master-0). In the next steps we will deploy etcd on the new masters and add them to the etcd cluster.&lt;/p&gt;

&lt;p&gt;The first step we need to update is the DNS and LoadBalancer in the AWS console.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Procedure:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To do so, get the IP address of the new servers in the AWS Console:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2020-05-28-how-to-replace-all-masters-in-ocp-cluster/aws-ec2-get-ip.png&quot; alt=&quot;Get Instance IP Address&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now open the HostedZone for OCP in Route53:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2020-05-28-how-to-replace-all-masters-in-ocp-cluster/aws-hosted-zones.png&quot; alt=&quot;AWS Hosted Zones&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Finally update the records etcd-1 and etcd-2 for this cluster:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2020-05-28-how-to-replace-all-masters-in-ocp-cluster/aws-update-zone.png&quot; alt=&quot;AWS Hosted Zones&quot; /&gt;&lt;/p&gt;

&lt;p&gt;LoadBalancer is automatically updated if you are running an IPI cluster. If you are using an UPI cluster, you should manually update your LB also.&lt;/p&gt;

&lt;h1 id=&quot;run-etcd-tokenize&quot;&gt;Run etcd tokenize&lt;/h1&gt;

&lt;p&gt;Now we need to start the etcd-signer in the master-0.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Procedure:&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/id_rsa core@10.0.140.240
&lt;span class=&quot;go&quot;&gt;... omitted ...
Last login: Fri May 22 11:08:03 2020 from 172.31.36.49
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-140-240 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc login https://api-int.ocp.rhbr-labs.com:6443
&lt;span class=&quot;go&quot;&gt;The server uses a certificate signed by an unknown authority.
You can bypass the certificate check, but any data you send to the server could be intercepted by others.
Use insecure connections? (y/n): y

Authentication required for https://api-int.ocp.rhbr-labs.com:6443 (openshift)
Username: kubeadmin
Password: 
Login successful.

You have access to 53 projects, the list has been suppressed. You can list all projects with &apos;oc projects&apos;

Using project &quot;default&quot;.
Welcome! See &apos;oc help&apos; to get started.
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-140-240 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KUBE_ETCD_SIGNER_SERVER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc adm release info &lt;span class=&quot;nt&quot;&gt;--image-for&lt;/span&gt; kube-etcd-signer-server &lt;span class=&quot;nt&quot;&gt;--registry-config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/lib/kubelet/config.json&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-140-240 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; /usr/local/bin/tokenize-signer.sh ip-10-0-140-240 &amp;lt;1&amp;gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-140-240 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc create &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; ./assets/manifests/kube-etcd-cert-signer.yaml
&lt;span class=&quot;go&quot;&gt;pod/etcd-signer created
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-140-240 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc get pods &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-config
&lt;span class=&quot;go&quot;&gt;NAME          READY   STATUS    RESTARTS   AGE
etcd-signer   1/1     Running   0          15s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&amp;lt;1&amp;gt; Hostname of our functional master. In our case it is master-0&lt;/p&gt;

&lt;h1 id=&quot;run-etcd-member-recover-for-new-members&quot;&gt;Run etcd-member-recover for new members&lt;/h1&gt;

&lt;p&gt;// TODO: SPEAK ABOUT THE BUG IN etcd-member-recover.sh SCRIPT&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Procedure:&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Copy etcdctl bin due bug &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;etcd-member-recover script
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;tar&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-xzf&lt;/span&gt; etcd-20200522-bkp.tgz
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;scp &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/id_rsa assets/bin/etcdctl core@ip-10-0-136-143:~/
&lt;span class=&quot;go&quot;&gt;etcdctl                                                                                                                                 100%   24MB  55.4MB/s   00:00    

&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;SSH to master-1
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/id_rsa core@ip-10-0-136-143.ec2.internal
&lt;span class=&quot;go&quot;&gt;... omitted ...
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; ~/assets/bin ~/assets/backup ~/assets/tmp
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mv &lt;/span&gt;etcdctl ~/assets/bin/
&lt;span class=&quot;go&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;vi /usr/local/bin/etcd-member-recover.sh
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;## COMMENT LINE BELOW (dl_etcdctl)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;function run {
  init
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;dl_etcdctl &amp;lt;&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; THIS LINE
&lt;span class=&quot;go&quot;&gt;  backup_manifest
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;  DISCOVERY_DOMAIN=$&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-oP&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;(?&amp;lt;=discovery-srv=).*[^&quot;]&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ASSET_DIR&lt;/span&gt;/backup/etcd-member.yaml &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;  if [ -z &quot;$&lt;/span&gt;DISCOVERY_DOMAIN&lt;span class=&quot;s2&quot;&gt;&quot; ]; then
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;    echo &quot;Discovery domain can not be extracted from $&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ASSET_DIR/backup/etcd-member.yaml&quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    exit 1
  fi

&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc login https://api-int.ocp.rhbr-labs.com:6443
&lt;span class=&quot;go&quot;&gt;The server uses a certificate signed by an unknown authority.
You can bypass the certificate check, but any data you send to the server could be intercepted by others.
Use insecure connections? (y/n): y

Authentication required for https://api-int.ocp.rhbr-labs.com:6443 (openshift)
Username: kubeadmin
Password: 
Login successful.

You have access to 53 projects, the list has been suppressed. You can list all projects with &apos;oc projects&apos;

Using project &quot;default&quot;.
Welcome! See &apos;oc help&apos; to get started.

&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SETUP_ETCD_ENVIRONMENT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc adm release info &lt;span class=&quot;nt&quot;&gt;--image-for&lt;/span&gt; machine-config-operator &lt;span class=&quot;nt&quot;&gt;--registry-config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/lib/kubelet/config.json&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KUBE_CLIENT_AGENT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc adm release info &lt;span class=&quot;nt&quot;&gt;--image-for&lt;/span&gt; kube-client-agent &lt;span class=&quot;nt&quot;&gt;--registry-config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/lib/kubelet/config.json&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; /usr/local/bin/etcd-member-recover.sh 10.0.140.240 etcd-member-ip-10-0-136-143.ec2.internal
&lt;span class=&quot;go&quot;&gt;Backing up /etc/kubernetes/manifests/etcd-member.yaml to ./assets/backup/
Backing up /etc/etcd/etcd.conf to ./assets/backup/
Trying to backup etcd client certs..
etcd client certs found in /etc/kubernetes/static-pod-resources/kube-apiserver-pod-6 backing up to ./assets/backup/
Stopping etcd..
Waiting for etcd-member to stop
... omitted ...
Waiting for generate-certs to stop
Patching etcd-member manifest..
Updating etcd membership..
Removing etcd data_dir /var/lib/etcd..
Member 2517d85f40558b47 added to cluster ea5a775da961a326

ETCD_NAME=&quot;etcd-member-ip-10-0-136-143.ec2.internal&quot;
ETCD_INITIAL_CLUSTER=&quot;etcd-member-ip-10-0-136-143.ec2.internal=https://etcd-1.ocp.rhbr-labs.com:2380,etcd-member-ip-10-0-140-240.ec2.internal=https://etcd-0.ocp.rhbr-labs.com:2380&quot;
ETCD_INITIAL_ADVERTISE_PEER_URLS=&quot;https://etcd-1.ocp.rhbr-labs.com:2380&quot;
ETCD_INITIAL_CLUSTER_STATE=&quot;existing&quot;
Starting etcd..

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&amp;lt;1&amp;gt; IP 10.0.136.116 is the server functional master, where etcd-signer is running. In our case this is the master-0.&lt;/p&gt;

&lt;p&gt;Now you have etcd functional in two masters (master-0 and master-1):&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc get pods &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-etcd
&lt;span class=&quot;go&quot;&gt;NAME                                       READY   STATUS     RESTARTS   AGE    IP             NODE                           NOMINATED NODE   READINESS GATES
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;etcd-member-ip-10-0-136-143.ec2.internal   2/2     Running    0          28s    10.0.136.143   ip-10-0-136-143.ec2.internal   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class=&quot;gp&quot;&gt;etcd-member-ip-10-0-137-235.ec2.internal   0/2     Init:1/2   5          38m    10.0.137.235   ip-10-0-137-235.ec2.internal   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class=&quot;gp&quot;&gt;etcd-member-ip-10-0-140-240.ec2.internal   2/2     Running    0          136m   10.0.140.240   ip-10-0-140-240.ec2.internal   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-etcd rsh etcd-member-ip-10-0-136-143.ec2.internal
&lt;span class=&quot;go&quot;&gt;Defaulting container name to etcd-member.
Use &apos;oc describe pod/etcd-member-ip-10-0-136-143.ec2.internal -n openshift-etcd&apos; to see all of the containers in this pod.
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;sh-4.2#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCDCTL_API&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3 &lt;span class=&quot;nv&quot;&gt;ETCDCTL_CACERT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/ssl/etcd/ca.crt &lt;span class=&quot;nv&quot;&gt;ETCDCTL_CERT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;find /etc/ssl/ &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;peer&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;crt&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ETCDCTL_KEY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;find /etc/ssl/ &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;peer&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;key&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;sh-4.2#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;etcdctl member list &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; table
&lt;span class=&quot;go&quot;&gt;+------------------+---------+------------------------------------------+---------------------------------------+---------------------------+
|        ID        | STATUS  |                   NAME                   |              PEER ADDRS               |       CLIENT ADDRS        |
+------------------+---------+------------------------------------------+---------------------------------------+---------------------------+
| 2517d85f40558b47 | started | etcd-member-ip-10-0-136-143.ec2.internal | https://etcd-1.ocp.rhbr-labs.com:2380 | https://10.0.136.143:2379 |
| 890a07c73df999b0 | started | etcd-member-ip-10-0-140-240.ec2.internal | https://etcd-0.ocp.rhbr-labs.com:2380 | https://10.0.140.240:2379 |
+------------------+---------+------------------------------------------+---------------------------------------+---------------------------+
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Repeat procedure above to master-2:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;scp &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/id_rsa assets/bin/etcdctl core@ip-10-0-137-235.ec2.internal:~/
&lt;span class=&quot;go&quot;&gt;Warning: Permanently added &apos;ip-10-0-137-235.ec2.internal,10.0.137.235&apos; (ECDSA) to the list of known hosts.
etcdctl                                                                                                                                 100%   24MB  51.6MB/s   00:00   
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-137-235 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; ~/assets/bin ~/assets/backup ~/assets/tmp
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-137-235 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mv &lt;/span&gt;etcdctl ~/assets/bin/
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-137-235 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;vi /usr/local/bin/etcd-member-recover.sh
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-137-235 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc login https://api-int.ocp.rhbr-labs.com:6443
&lt;span class=&quot;go&quot;&gt;... omitted ...
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-137-235 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SETUP_ETCD_ENVIRONMENT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc adm release info &lt;span class=&quot;nt&quot;&gt;--image-for&lt;/span&gt; machine-config-operator &lt;span class=&quot;nt&quot;&gt;--registry-config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/lib/kubelet/config.json&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-137-235 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KUBE_CLIENT_AGENT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc adm release info &lt;span class=&quot;nt&quot;&gt;--image-for&lt;/span&gt; kube-client-agent &lt;span class=&quot;nt&quot;&gt;--registry-config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/lib/kubelet/config.json&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-137-235 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; /usr/local/bin/etcd-member-recover.sh 10.0.140.240 etcd-member-ip-10-0-137-235.ec2.internal
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-137-235 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; /usr/local/bin/etcd-member-recover.sh 10.0.140.240 etcd-member-ip-10-0-137-235.ec2.internal
&lt;span class=&quot;go&quot;&gt;Backing up /etc/kubernetes/manifests/etcd-member.yaml to ./assets/backup/
Backing up /etc/etcd/etcd.conf to ./assets/backup/
Trying to backup etcd client certs..
etcd client certs found in /etc/kubernetes/static-pod-resources/kube-apiserver-pod-6 backing up to ./assets/backup/
Stopping etcd..
... omitted ...
Member f03ec7613f440ab8 added to cluster ea5a775da961a326

ETCD_NAME=&quot;etcd-member-ip-10-0-137-235.ec2.internal&quot;
ETCD_INITIAL_CLUSTER=&quot;etcd-member-ip-10-0-136-143.ec2.internal=https://etcd-1.ocp.rhbr-labs.com:2380,etcd-member-ip-10-0-140-240.ec2.internal=https://etcd-0.ocp.rhbr-labs.com:2380,etcd-member-ip-10-0-137-235.ec2.internal=https://etcd-2.ocp.rhbr-labs.com:2380&quot;
ETCD_INITIAL_ADVERTISE_PEER_URLS=&quot;https://etcd-2.ocp.rhbr-labs.com:2380&quot;
ETCD_INITIAL_CLUSTER_STATE=&quot;existing&quot;
Starting etcd..
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-137-235 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc get pods &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-etcd
&lt;span class=&quot;go&quot;&gt;NAME                                       READY   STATUS    RESTARTS   AGE     IP             NODE                           NOMINATED NODE   READINESS GATES
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;etcd-member-ip-10-0-136-143.ec2.internal   2/2     Running   0          8m28s   10.0.136.143   ip-10-0-136-143.ec2.internal   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class=&quot;gp&quot;&gt;etcd-member-ip-10-0-137-235.ec2.internal   2/2     Running   0          42s     10.0.137.235   ip-10-0-137-235.ec2.internal   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class=&quot;gp&quot;&gt;etcd-member-ip-10-0-140-240.ec2.internal   2/2     Running   0          144m    10.0.140.240   ip-10-0-140-240.ec2.internal   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-137-235 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-etcd rsh etcd-member-ip-10-0-137-235.ec2.internal
&lt;span class=&quot;go&quot;&gt;Defaulting container name to etcd-member.
Use &apos;oc describe pod/etcd-member-ip-10-0-137-235.ec2.internal -n openshift-etcd&apos; to see all of the containers in this pod.
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;sh-4.2#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCDCTL_API&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3 &lt;span class=&quot;nv&quot;&gt;ETCDCTL_CACERT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/ssl/etcd/ca.crt &lt;span class=&quot;nv&quot;&gt;ETCDCTL_CERT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;find /etc/ssl/ &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;peer&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;crt&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ETCDCTL_KEY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;find /etc/ssl/ &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;peer&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;key&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;sh-4.2#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;etcdctl member list &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; table
&lt;span class=&quot;go&quot;&gt;+------------------+---------+------------------------------------------+---------------------------------------+---------------------------+
|        ID        | STATUS  |                   NAME                   |              PEER ADDRS               |       CLIENT ADDRS        |
+------------------+---------+------------------------------------------+---------------------------------------+---------------------------+
| 2517d85f40558b47 | started | etcd-member-ip-10-0-136-143.ec2.internal | https://etcd-1.ocp.rhbr-labs.com:2380 | https://10.0.136.143:2379 |
| 890a07c73df999b0 | started | etcd-member-ip-10-0-140-240.ec2.internal | https://etcd-0.ocp.rhbr-labs.com:2380 | https://10.0.140.240:2379 |
| f03ec7613f440ab8 | started | etcd-member-ip-10-0-137-235.ec2.internal | https://etcd-2.ocp.rhbr-labs.com:2380 | https://10.0.137.235:2379 |
+------------------+---------+------------------------------------------+---------------------------------------+---------------------------+

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Approve pending certificates:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get csr    
&lt;span class=&quot;go&quot;&gt;NAME                                          AGE    REQUESTOR                                                                   CONDITION                                
csr-6dns4                                     69m    system:node:ip-10-0-136-143.ec2.internal                                    Approved,Issued                         
csr-7g7b9                                     168m   system:node:ip-10-0-163-43.ec2.internal                                     Approved,Issued                         
csr-829pm                                     160m   system:serviceaccount:openshift-machine-config-operator:node-bootstrapper   Approved,Issued                         
csr-d5q44                                     160m   system:node:ip-10-0-148-0.ec2.internal                                      Approved,Issued                         
csr-gxdhg                                     168m   system:serviceaccount:openshift-machine-config-operator:node-bootstrapper   Approved,Issued          
... omitted ...
system:etcd-server:etcd-1.ocp.rhbr-labs.com   46m    system:serviceaccount:openshift-machine-config-operator:node-bootstrapper   Pending                                 
system:etcd-server:etcd-2.ocp.rhbr-labs.com   47m    system:serviceaccount:openshift-machine-config-operator:node-bootstrapper   Pending           

&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get csr &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; name | xargs oc adm certificate approve    
&lt;span class=&quot;go&quot;&gt;certificatesigningrequest.certificates.k8s.io/system:etcd-server:etcd-1.ocp.rhbr-labs.com approved                                                                       
certificatesigningrequest.certificates.k8s.io/system:etcd-server:etcd-2.ocp.rhbr-labs.com approved                  
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That’s all you need to recover a cluster of masters. The steps below is only to exercise this process a little bit more.&lt;/p&gt;

&lt;h1 id=&quot;replace-master-0etcd-0&quot;&gt;Replace master-0/etcd-0&lt;/h1&gt;

&lt;p&gt;This step is not required. This is only for testing purposes.&lt;/p&gt;

&lt;h2 id=&quot;remove-the-etcd-member-etcd-0&quot;&gt;Remove the etcd member etcd-0&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Procedure:&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;SSH to master-1 or master-2
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/id_rsa core@ip-10-0-137-235.ec2.internal
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Run script to remove etcd-0 from the cluster
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-137-235 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; /usr/local/bin/etcd-member-remove.sh etcd-member-ip-10-0-140-240.ec2.internal
&lt;span class=&quot;go&quot;&gt;22e5bfd54e0e25533d7fb5214561968d112d4ab5dc2cb2db993783e78a535e6e
etcdctl version: 3.3.17
API version: 3.3
Trying to backup etcd client certs..
etcd client certs already backed up and available ./assets/backup/
Member 890a07c73df999b0 removed from cluster ea5a775da961a326
etcd member etcd-member-ip-10-0-140-240.ec2.internal with 890a07c73df999b0 successfully removed..
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;remove-server-master-0&quot;&gt;Remove server master-0&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Procedure:&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc delete machine &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;master-0 | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{ print $1 }&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;machine.machine.openshift.io &quot;ocp-w2lhz-master-0&quot; deleted
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the step below, sometimes you will see that OpenShift will be stuck in Deleting phase.&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get machines &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api
&lt;span class=&quot;go&quot;&gt;NAME                                PHASE      TYPE        REGION      ZONE         AGE                                                                                  
ocp-w2lhz-master-0                  Deleting   m4.xlarge   us-east-1   us-east-1a   157m                                                                                 
ocp-w2lhz-master-1                  Running    m4.xlarge   us-east-1   us-east-1a   61m                                                                                  
ocp-w2lhz-master-2                  Running    m4.xlarge   us-east-1   us-east-1a   61m                                                                                  
ocp-w2lhz-worker-us-east-1a-nq6g2   Running    m4.large    us-east-1   us-east-1a   153m                                                                                 
ocp-w2lhz-worker-us-east-1b-chzl6   Running    m4.large    us-east-1   us-east-1b   153m                                                                                 
ocp-w2lhz-worker-us-east-1c-6zdpt   Running    m4.large    us-east-1   us-east-1c   153m 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you get this problem, terminate the server manually on AWS and it will disappear in OpenShift after some minutes.&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get machine &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api 
&lt;span class=&quot;go&quot;&gt;NAME                                PHASE     TYPE        REGION      ZONE         AGE
ocp-w2lhz-master-1                  Running   m4.xlarge   us-east-1   us-east-1a   71m
ocp-w2lhz-master-2                  Running   m4.xlarge   us-east-1   us-east-1a   71m
ocp-w2lhz-worker-us-east-1a-nq6g2   Running   m4.large    us-east-1   us-east-1a   162m
ocp-w2lhz-worker-us-east-1b-chzl6   Running   m4.large    us-east-1   us-east-1b   162m
ocp-w2lhz-worker-us-east-1c-6zdpt   Running   m4.large    us-east-1   us-east-1c   162m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;create-a-new-server-master-0&quot;&gt;Create a new server master-0&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Procedure:&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;vi new-master-0.yaml
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Remove entire &lt;span class=&quot;s2&quot;&gt;&quot;status&quot;&lt;/span&gt; section
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Remove the providerID field
&lt;span class=&quot;go&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc create &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; new-master-0.yaml
&lt;span class=&quot;go&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Wait &lt;span class=&quot;k&quot;&gt;until &lt;/span&gt;you get it &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;Running state:
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get machine &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api 
&lt;span class=&quot;go&quot;&gt;NAME                                PHASE          TYPE        REGION      ZONE         AGE
ocp-w2lhz-master-0                  Running   m4.xlarge   us-east-1   us-east-1a   9s
ocp-w2lhz-master-1                  Running        m4.xlarge   us-east-1   us-east-1a   77m
ocp-w2lhz-master-2                  Running        m4.xlarge   us-east-1   us-east-1a   77m
ocp-w2lhz-worker-us-east-1a-nq6g2   Running        m4.large    us-east-1   us-east-1a   169m
ocp-w2lhz-worker-us-east-1b-chzl6   Running        m4.large    us-east-1   us-east-1b   169m
ocp-w2lhz-worker-us-east-1c-6zdpt   Running        m4.large    us-east-1   us-east-1c   169m

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;update-dns-and-lb-records-1&quot;&gt;Update DNS and LB records&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Procedure:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2020-05-28-how-to-replace-all-masters-in-ocp-cluster/aws-ec2-get-ip.png&quot; alt=&quot;Get Instance IP Address&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now open the HostedZone for OCP in Route53:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2020-05-28-how-to-replace-all-masters-in-ocp-cluster/aws-hosted-zones.png&quot; alt=&quot;AWS Hosted Zones&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Finally update the records etcd-1 and etcd-2 for this cluster:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2020-05-28-how-to-replace-all-masters-in-ocp-cluster/aws-update-zone.png&quot; alt=&quot;AWS Hosted Zones&quot; /&gt;&lt;/p&gt;

&lt;p&gt;LoadBalancer is automatically updated if you are running an IPI cluster. If you are using an UPI cluster, you should manually update your LB also.&lt;/p&gt;

&lt;h1 id=&quot;run-etcd-tokenize-1&quot;&gt;Run etcd tokenize&lt;/h1&gt;

&lt;p&gt;Now we need to start the etcd-signer in the master-1.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Procedure:&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/id_rsa core@ip-10-0-136-143.ec2.internal
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc login https://api-int.ocp.rhbr-labs.com:6443
&lt;span class=&quot;go&quot;&gt;Authentication required for https://api-int.ocp.rhbr-labs.com:6443 (openshift)
Username: kubeadmin
Password: 
Login successful.

You have access to 53 projects, the list has been suppressed. You can list all projects with &apos;oc projects&apos;

Using project &quot;default&quot;.
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KUBE_ETCD_SIGNER_SERVER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc adm release info &lt;span class=&quot;nt&quot;&gt;--image-for&lt;/span&gt; kube-etcd-signer-server &lt;span class=&quot;nt&quot;&gt;--registry-config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/lib/kubelet/config.json&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; ./assets/manifests
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; /usr/local/bin/tokenize-signer.sh ip-10-0-136-143
&lt;span class=&quot;go&quot;&gt;Populating template /usr/local/share/openshift-recovery/template/kube-etcd-cert-signer.yaml.template
Populating template ./assets/tmp/kube-etcd-cert-signer.yaml.stage1
Tokenized template now ready: ./assets/manifests/kube-etcd-cert-signer.yaml
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc create &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; ./assets/manifests/kube-etcd-cert-signer.yaml
&lt;span class=&quot;go&quot;&gt;pod/etcd-signer created
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-136-143 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc get pods &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-config
&lt;span class=&quot;go&quot;&gt;NAME          READY   STATUS    RESTARTS   AGE
etcd-signer   1/1     Running   0          16s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;run-etcd-member-recover-for-new-the-member&quot;&gt;Run etcd-member-recover for new the member&lt;/h1&gt;

&lt;p&gt;// TODO: SPEAK ABOUT THE BUG IN etcd-member-recover.sh SCRIPT&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Procedure:&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;Copy etcdctl bin due bug &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;etcd-member-recover script
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;tar&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-xzf&lt;/span&gt; etcd-20200522-bkp.tgz
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;scp &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/id_rsa assets/bin/etcdctl core@ip-10-0-130-22.ec2.internal:~/
&lt;span class=&quot;go&quot;&gt;etcdctl                                                                                                                                 100%   24MB  56.3MB/s   00:00    

&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;SSH to master-0
&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/id_rsa core@ip-10-0-130-22.ec2.internal   
&lt;span class=&quot;go&quot;&gt;... omitted ...
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-130-22 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; ~/assets/bin ~/assets/backup ~/assets/tmp
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-130-22 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mv &lt;/span&gt;etcdctl ~/assets/bin/
&lt;span class=&quot;go&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-130-22 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;vi /usr/local/bin/etcd-member-recover.sh
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;## COMMENT LINE BELOW (dl_etcdctl)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;function run {
  init
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;dl_etcdctl &amp;lt;&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; THIS LINE
&lt;span class=&quot;go&quot;&gt;  backup_manifest
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;  DISCOVERY_DOMAIN=$&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-oP&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;(?&amp;lt;=discovery-srv=).*[^&quot;]&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ASSET_DIR&lt;/span&gt;/backup/etcd-member.yaml &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;  if [ -z &quot;$&lt;/span&gt;DISCOVERY_DOMAIN&lt;span class=&quot;s2&quot;&gt;&quot; ]; then
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;    echo &quot;Discovery domain can not be extracted from $&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ASSET_DIR/backup/etcd-member.yaml&quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    exit 1
  fi

&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-130-22 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc login https://api-int.ocp.rhbr-labs.com:6443
&lt;span class=&quot;go&quot;&gt;The server uses a certificate signed by an unknown authority.
You can bypass the certificate check, but any data you send to the server could be intercepted by others.
Use insecure connections? (y/n): y

Authentication required for https://api-int.ocp.rhbr-labs.com:6443 (openshift)
Username: kubeadmin
Password: 
Login successful.

You have access to 53 projects, the list has been suppressed. You can list all projects with &apos;oc projects&apos;

Using project &quot;default&quot;.
Welcome! See &apos;oc help&apos; to get started.

&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-130-22 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SETUP_ETCD_ENVIRONMENT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc adm release info &lt;span class=&quot;nt&quot;&gt;--image-for&lt;/span&gt; machine-config-operator &lt;span class=&quot;nt&quot;&gt;--registry-config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/lib/kubelet/config.json&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-130-22 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KUBE_CLIENT_AGENT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc adm release info &lt;span class=&quot;nt&quot;&gt;--image-for&lt;/span&gt; kube-client-agent &lt;span class=&quot;nt&quot;&gt;--registry-config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/lib/kubelet/config.json&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-130-22 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; /usr/local/bin/etcd-member-recover.sh 10.0.136.143 etcd-member-ip-10-0-130-22.ec2.internal &amp;lt;1&amp;gt;
&lt;span class=&quot;go&quot;&gt;Backing up /etc/kubernetes/manifests/etcd-member.yaml to ./assets/backup/
Backing up /etc/etcd/etcd.conf to ./assets/backup/
Trying to backup etcd client certs..
etcd client certs found in /etc/kubernetes/static-pod-resources/kube-apiserver-pod-6 backing up to ./assets/backup/
Stopping etcd..
... omitted ...
Waiting for generate-certs to stop
Patching etcd-member manifest..
Updating etcd membership..
Removing etcd data_dir /var/lib/etcd..
Member 2edf74688e8d0666 added to cluster ea5a775da961a326

ETCD_NAME=&quot;etcd-member-ip-10-0-130-22.ec2.internal&quot;
ETCD_INITIAL_CLUSTER=&quot;etcd-member-ip-10-0-136-143.ec2.internal=https://etcd-1.ocp.rhbr-labs.com:2380,etcd-member-ip-10-0-130-22.ec2.internal=https://etcd-0.ocp.rhbr-labs.com:2380,etcd-member-ip-10-0-137-235.ec2.internal=https://etcd-2.ocp.rhbr-labs.com:2380&quot;
ETCD_INITIAL_ADVERTISE_PEER_URLS=&quot;https://etcd-0.ocp.rhbr-labs.com:2380&quot;
ETCD_INITIAL_CLUSTER_STATE=&quot;existing&quot;
Starting etcd..

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&amp;lt;1&amp;gt; IP 10.0.136.143 is the server functional master, where etcd-signer is running. In our case this is the master-2.&lt;/p&gt;

&lt;p&gt;Now you have etcd functional all 3 masters again:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-130-22 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc get pods &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-etcd
&lt;span class=&quot;go&quot;&gt;NAME                                       READY   STATUS    RESTARTS   AGE   IP             NODE                           NOMINATED NODE   READINESS GATES
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;etcd-member-ip-10-0-130-22.ec2.internal    2/2     Running   0          80s   10.0.130.22    ip-10-0-130-22.ec2.internal    &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class=&quot;gp&quot;&gt;etcd-member-ip-10-0-136-143.ec2.internal   2/2     Running   0          54m   10.0.136.143   ip-10-0-136-143.ec2.internal   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class=&quot;gp&quot;&gt;etcd-member-ip-10-0-137-235.ec2.internal   2/2     Running   0          46m   10.0.137.235   ip-10-0-137-235.ec2.internal   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&amp;lt;none&amp;gt;
&lt;span class=&quot;gp&quot;&gt;[core@ip-10-0-130-22 ~]$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;oc &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-etcd rsh etcd-member-ip-10-0-130-22.ec2.internal
&lt;span class=&quot;go&quot;&gt;Defaulting container name to etcd-member.
Use &apos;oc describe pod/etcd-member-ip-10-0-130-22.ec2.internal -n openshift-etcd&apos; to see all of the containers in this pod.
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;sh-4.2#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCDCTL_API&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3 &lt;span class=&quot;nv&quot;&gt;ETCDCTL_CACERT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/ssl/etcd/ca.crt &lt;span class=&quot;nv&quot;&gt;ETCDCTL_CERT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;find /etc/ssl/ &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;peer&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;crt&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ETCDCTL_KEY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;find /etc/ssl/ &lt;span class=&quot;nt&quot;&gt;-name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;peer&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;key&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;sh-4.2#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;etcdctl member list &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; table
&lt;span class=&quot;go&quot;&gt;+------------------+---------+------------------------------------------+---------------------------------------+---------------------------+
|        ID        | STATUS  |                   NAME                   |              PEER ADDRS               |       CLIENT ADDRS        |
+------------------+---------+------------------------------------------+---------------------------------------+---------------------------+
| 2517d85f40558b47 | started | etcd-member-ip-10-0-136-143.ec2.internal | https://etcd-1.ocp.rhbr-labs.com:2380 | https://10.0.136.143:2379 |
| 2edf74688e8d0666 | started |  etcd-member-ip-10-0-130-22.ec2.internal | https://etcd-0.ocp.rhbr-labs.com:2380 |  https://10.0.130.22:2379 |
| f03ec7613f440ab8 | started | etcd-member-ip-10-0-137-235.ec2.internal | https://etcd-2.ocp.rhbr-labs.com:2380 | https://10.0.137.235:2379 |
+------------------+---------+------------------------------------------+---------------------------------------+---------------------------+

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Remove etcd-signer pod:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc delete pod etcd-signer &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-config
&lt;span class=&quot;go&quot;&gt;pod &quot;etcd-signer&quot; deleted
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;validating-cluster&quot;&gt;Validating cluster&lt;/h1&gt;

&lt;p&gt;Approve any pending certificates:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get csr | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;Pending
&lt;span class=&quot;go&quot;&gt;system:etcd-server:etcd-0.ocp.rhbr-labs.com   13m     system:serviceaccount:openshift-machine-config-operator:node-bootstrapper   Pending
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get csr &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; name | xargs oc adm certificate approve
&lt;span class=&quot;go&quot;&gt;certificatesigningrequest.certificates.k8s.io/system:etcd-server:etcd-0.ocp.rhbr-labs.com approved  
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Check nodes status:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get nodes
&lt;span class=&quot;go&quot;&gt;NAME                           STATUS   ROLES    AGE     VERSION
ip-10-0-129-242.ec2.internal   Ready    worker   3h10m   v1.16.2
ip-10-0-130-22.ec2.internal    Ready    master   21m     v1.16.2
ip-10-0-136-143.ec2.internal   Ready    master   99m     v1.16.2
ip-10-0-137-235.ec2.internal   Ready    master   99m     v1.16.2
ip-10-0-148-0.ec2.internal     Ready    worker   3h10m   v1.16.2
ip-10-0-161-99.ec2.internal    Ready    worker   3h10m   v1.16.2
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get machine &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-machine-api &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide
&lt;span class=&quot;go&quot;&gt;NAME                                PHASE     TYPE        REGION      ZONE         AGE     NODE                           PROVIDERID                              STATE
ocp-w2lhz-master-0                  Running   m4.xlarge   us-east-1   us-east-1a   25m     ip-10-0-130-22.ec2.internal    aws:///us-east-1a/i-02040b6466f85e292   running
ocp-w2lhz-master-1                  Running   m4.xlarge   us-east-1   us-east-1a   102m    ip-10-0-136-143.ec2.internal   aws:///us-east-1a/i-06626c50318669add   running
ocp-w2lhz-master-2                  Running   m4.xlarge   us-east-1   us-east-1a   102m    ip-10-0-137-235.ec2.internal   aws:///us-east-1a/i-07037feaed2c7af5c   running
ocp-w2lhz-worker-us-east-1a-nq6g2   Running   m4.large    us-east-1   us-east-1a   3h14m   ip-10-0-129-242.ec2.internal   aws:///us-east-1a/i-01730585eb2f8c877   running
ocp-w2lhz-worker-us-east-1b-chzl6   Running   m4.large    us-east-1   us-east-1b   3h14m   ip-10-0-148-0.ec2.internal     aws:///us-east-1b/i-0c7998b00f3be1800   running
ocp-w2lhz-worker-us-east-1c-6zdpt   Running   m4.large    us-east-1   us-east-1c   3h14m   ip-10-0-161-99.ec2.internal    aws:///us-east-1c/i-0b771047194746d16   running
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Check the ClusterOperator status:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get co
&lt;span class=&quot;go&quot;&gt;NAME                                       VERSION   AVAILABLE   PROGRESSING   DEGRADED   SINCE
authentication                             4.3.22    True        False         False      179m
cloud-credential                           4.3.22    True        False         False      3h15m
cluster-autoscaler                         4.3.22    True        False         False      3h7m
console                                    4.3.22    True        False         False      3h2m
dns                                        4.3.22    True        False         False      3h11m
image-registry                             4.3.22    True        False         False      3h5m
ingress                                    4.3.22    True        False         False      3h5m
insights                                   4.3.22    True        False         False      3h7m
kube-apiserver                             4.3.22    True        False         False      3h9m
kube-controller-manager                    4.3.22    True        False         False      3h9m
kube-scheduler                             4.3.22    True        False         False      3h9m
machine-api                                4.3.22    True        False         False      3h11m
machine-config                             4.3.22    True        False         False      3h10m
marketplace                                4.3.22    True        False         False      37m
monitoring                                 4.3.22    True        False         False      102m
network                                    4.3.22    True        False         False      3h11m
node-tuning                                4.3.22    True        False         False      38m
openshift-apiserver                        4.3.22    True        False         False      31m
openshift-controller-manager               4.3.22    True        False         False      3h10m
openshift-samples                          4.3.22    True        False         False      3h6m
operator-lifecycle-manager                 4.3.22    True        False         False      3h8m
operator-lifecycle-manager-catalog         4.3.22    True        False         False      3h8m
operator-lifecycle-manager-packageserver   4.3.22    True        False         False      38m
service-ca                                 4.3.22    True        False         False      3h11m
service-catalog-apiserver                  4.3.22    True        False         False      3h8m
service-catalog-controller-manager         4.3.22    True        False         False      3h8m
storage                                    4.3.22    True        False         False      3h7m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Check etcd status:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get pods &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-etcd
&lt;span class=&quot;go&quot;&gt;NAME                                       READY   STATUS    RESTARTS   AGE
etcd-member-ip-10-0-130-22.ec2.internal    2/2     Running   0          6m32s
etcd-member-ip-10-0-136-143.ec2.internal   2/2     Running   0          59m
etcd-member-ip-10-0-137-235.ec2.internal   2/2     Running   0          51m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Check API pods:&lt;/p&gt;
&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get pods &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-apiserver
&lt;span class=&quot;go&quot;&gt;NAME              READY   STATUS    RESTARTS   AGE
apiserver-4wq9s   1/1     Running   0          21m
apiserver-ljz4t   1/1     Running   0          98m
apiserver-zrntw   1/1     Running   0          98m
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;[root@ocp-bastion recover-master-2]#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;oc get pods &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-kube-apiserver
&lt;span class=&quot;go&quot;&gt;NAME                                             READY   STATUS      RESTARTS   AGE
installer-6-ip-10-0-130-22.ec2.internal          0/1     Completed   0          21m
installer-6-ip-10-0-136-143.ec2.internal         0/1     Completed   0          96m
installer-6-ip-10-0-137-235.ec2.internal         0/1     Completed   0          98m
kube-apiserver-ip-10-0-130-22.ec2.internal       3/3     Running     0          20m
kube-apiserver-ip-10-0-136-143.ec2.internal      3/3     Running     1          96m
kube-apiserver-ip-10-0-137-235.ec2.internal      3/3     Running     1          97m
revision-pruner-6-ip-10-0-130-22.ec2.internal    0/1     Completed   0          19m
revision-pruner-6-ip-10-0-136-143.ec2.internal   0/1     Completed   0          96m
revision-pruner-6-ip-10-0-137-235.ec2.internal   0/1     Completed   0          96m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 27 May 2020 21:05:55 +0000</pubDate>
        <link>https://giofontana.github.io/blog/how-to-restore-all-masters/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/how-to-restore-all-masters/</guid>
      </item>
    
      <item>
        <title>CloudForms Database High Availability Explained</title>
        <description>&lt;p&gt;The following diagram describes our setup.&lt;/p&gt;

&lt;p&gt;In this architecture, server A is the primary VMDB, server B is the standby, and C1 to C4 are workers connected to server A database. The CloudForms HA mechanism is based on two elements: repmgr and evm failover monitor.&lt;/p&gt;

&lt;p&gt;The repmgr tool (see &lt;a href=&quot;https://repmgr.org/&quot;&gt;https://repmgr.org/&lt;/a&gt;) runs on both VMDB appliances to keep the databases synchronized.&lt;/p&gt;

&lt;p&gt;The evm-failover-monitor tool runs on all workers and identifies when the primary VMDB is down to point workers to the standby VMDB.&lt;/p&gt;

&lt;p&gt;Let’s simulate a failure on our primary database.&lt;/p&gt;

&lt;p&gt;Repmgr tool in server B identifies that server A is down and promotes itself as primary VMDB.&lt;/p&gt;

&lt;p&gt;The evm-failover-monitor services, running on worker appliances C1 to C4, notice that server A is down and that standby server B was promoted. evm-failover-monitor reconfigures each worker’s database setup by modifying their configuration file database.yml to point to the new primary server B.&lt;/p&gt;

&lt;p&gt;Server B is now our primary database. Server A needs to be manually reintroduced to the cluster as the standby vmdb.&lt;/p&gt;

&lt;p&gt;Additional details on HA deployment procedures and configuration can be found on the Red Hat CloudForms High Availability Guide.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;The Red Hat CloudForms database high availability feature explained in this article is active/passive. The solution uses simple tools, is easy to configure, and is cost effective (as opposed to solutions using HAProxy, VIP, keepalived, etc).&lt;/p&gt;

&lt;p&gt;Being active/passive means that we might observe few seconds of unavailability (*) while repmgr and evm-failover-monitor tools detect the primary vmdb failure and promote the standby vmdb as primary. This minor downtime is acceptable in most cases.&lt;/p&gt;

&lt;p&gt;(*) The frequency with which CloudForms checks for database failover can be configured in the vmdb/config/ha_admin.yml configuration file.&lt;/p&gt;
</description>
        <pubDate>Thu, 22 Feb 2018 21:05:55 +0000</pubDate>
        <link>https://giofontana.github.io/blog/2018-02-23-cloudforms-database-ha/</link>
        <guid isPermaLink="true">https://giofontana.github.io/blog/2018-02-23-cloudforms-database-ha/</guid>
      </item>
    

    
      
        
      
    
      
    
      
        
          <item>
            <title>ACM</title>
            <description>&lt;h5&gt; Posts by Category : ACM &lt;/h5&gt;

&lt;div class=&quot;card&quot;&gt;
  
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;25 Oct 2020&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;/blog/2020-10-26-gitops-pipelines-acm-2/&quot;&gt;GitOps Using Red Hat OpenShift Pipelines (Tekton) and Red Hat Advanced Cluster Management&lt;/a&gt;&lt;/li&gt;
  
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;03 Sep 2020&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;/blog/2020-09-04-gitops-pipelines-acm/&quot;&gt;GitOps Using Red Hat OpenShift Pipelines (Tekton) and Red Hat Advanced Cluster Management&lt;/a&gt;&lt;/li&gt;
  
&lt;/div&gt;
</description>
            <link>https://giofontana.github.io/blog/categories/ACM/</link>
          </item>
        
      
    
      
        
          <item>
            <title></title>
            <description>&lt;h3&gt;   &lt;/h3&gt;

&lt;div id=&quot;categories&quot;&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#CloudForms&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/CloudForms&quot;&gt;CloudForms&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;CloudForms&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2018-02-23-cloudforms-database-ha/&quot;&gt;CloudForms Database High Availability Explained&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#OpenShift&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/OpenShift&quot;&gt;OpenShift&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;OpenShift&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2026-03-16-automating-openshift-certificates-with-claude-code/&quot;&gt;Cert expired again! How I quickly renewed my OpenShift cluster&apos;s certificate and automated renewals with Claude Code!&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/automate-vm-golden-image-openshift-virt-packer/&quot;&gt;Automate VM golden image builds for OpenShift with Packer&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2025-09-03-effective-observability/&quot;&gt;Effective observability with Red Hat build of OpenTelemetry | Red Hat Developer&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2022-11-11-book-announcement/&quot;&gt;New Book Announcement - OpenShift Multi-Cluster Management Handbook&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2022-08-31-rosa-transit-gateway/&quot;&gt;Deploying a Private OpenShift Cluster on AWS using Transit Gateway&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2021-06-28-windows-container-openshift/&quot;&gt;Windows Containers on Red Hat OpenShift: Does That Make Sense?&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2020-10-26-gitops-pipelines-acm-2/&quot;&gt;GitOps Using Red Hat OpenShift Pipelines (Tekton) and Red Hat Advanced Cluster Management&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2020-09-04-gitops-pipelines-acm/&quot;&gt;GitOps Using Red Hat OpenShift Pipelines (Tekton) and Red Hat Advanced Cluster Management&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/how-to-restore-all-masters/&quot;&gt;How to Restore All Masters in OpenShift 4.x&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#ACM&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/ACM&quot;&gt;ACM&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;ACM&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2020-10-26-gitops-pipelines-acm-2/&quot;&gt;GitOps Using Red Hat OpenShift Pipelines (Tekton) and Red Hat Advanced Cluster Management&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2020-09-04-gitops-pipelines-acm/&quot;&gt;GitOps Using Red Hat OpenShift Pipelines (Tekton) and Red Hat Advanced Cluster Management&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#Tekton&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/Tekton&quot;&gt;Tekton&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;Tekton&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2020-10-26-gitops-pipelines-acm-2/&quot;&gt;GitOps Using Red Hat OpenShift Pipelines (Tekton) and Red Hat Advanced Cluster Management&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2020-09-04-gitops-pipelines-acm/&quot;&gt;GitOps Using Red Hat OpenShift Pipelines (Tekton) and Red Hat Advanced Cluster Management&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#GitOps&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/GitOps&quot;&gt;GitOps&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;GitOps&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2026-03-16-automating-openshift-certificates-with-claude-code/&quot;&gt;Cert expired again! How I quickly renewed my OpenShift cluster&apos;s certificate and automated renewals with Claude Code!&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2020-10-26-gitops-pipelines-acm-2/&quot;&gt;GitOps Using Red Hat OpenShift Pipelines (Tekton) and Red Hat Advanced Cluster Management&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2020-09-04-gitops-pipelines-acm/&quot;&gt;GitOps Using Red Hat OpenShift Pipelines (Tekton) and Red Hat Advanced Cluster Management&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#Cloud&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/Cloud&quot;&gt;Cloud&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;Cloud&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2020-12-14-multicloud-journey/&quot;&gt;The start of the business on the multicloud journey&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#AWS&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/AWS&quot;&gt;AWS&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;AWS&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2022-08-31-rosa-transit-gateway/&quot;&gt;Deploying a Private OpenShift Cluster on AWS using Transit Gateway&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#My Life&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/My Life&quot;&gt;My Life&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;My Life&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2022-10-18-nothing-is-permanent/&quot;&gt;Nothing is permanent except change!&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#Artificial Inteligence&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/Artificial Inteligence&quot;&gt;Artificial Inteligence&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;Artificial Inteligence&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2026-02-03-cost-of-ai/&quot;&gt;The Cost of Generative AI&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2024-07-30-applied-data-science-mit-program/&quot;&gt;Applied Data Science Program: Levaraging AI for Effective Decision Making&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#Observability&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/Observability&quot;&gt;Observability&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;Observability&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2025-09-03-effective-observability/&quot;&gt;Effective observability with Red Hat build of OpenTelemetry | Red Hat Developer&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#OpenTelemetry&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/OpenTelemetry&quot;&gt;OpenTelemetry&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;OpenTelemetry&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2025-09-03-effective-observability/&quot;&gt;Effective observability with Red Hat build of OpenTelemetry | Red Hat Developer&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#Kubevirt&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/Kubevirt&quot;&gt;Kubevirt&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;Kubevirt&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/automate-vm-golden-image-openshift-virt-packer/&quot;&gt;Automate VM golden image builds for OpenShift with Packer&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#Packer&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/Packer&quot;&gt;Packer&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;Packer&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/automate-vm-golden-image-openshift-virt-packer/&quot;&gt;Automate VM golden image builds for OpenShift with Packer&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#Generative AI&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/Generative AI&quot;&gt;Generative AI&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;Generative AI&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2026-02-03-cost-of-ai/&quot;&gt;The Cost of Generative AI&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#DevOps&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/DevOps&quot;&gt;DevOps&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;DevOps&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2026-03-16-automating-openshift-certificates-with-claude-code/&quot;&gt;Cert expired again! How I quickly renewed my OpenShift cluster&apos;s certificate and automated renewals with Claude Code!&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

  &lt;div class=&quot;category-box&quot;&gt;
    
    &lt;div id=&quot;#AI&quot;&gt;&lt;/div&gt;
    &lt;h4 class=&quot;category-head&quot;&gt;&lt;a href=&quot;/blog/categories/AI&quot;&gt;AI&lt;/a&gt;&lt;/h4&gt;
    &lt;a name=&quot;AI&quot;&gt;&lt;/a&gt;
     
    &lt;article class=&quot;center&quot;&gt;
      &lt;h6&gt;&lt;a href=&quot;/blog/2026-03-16-automating-openshift-certificates-with-claude-code/&quot;&gt;Cert expired again! How I quickly renewed my OpenShift cluster&apos;s certificate and automated renewals with Claude Code!&lt;/a&gt;&lt;/h6&gt;
    &lt;/article&gt;


    

  &lt;/div&gt;

&lt;/div&gt;

</description>
            <link>https://giofontana.github.io/blog/categories/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Artificial Inteligence</title>
            <description>&lt;h5&gt; Posts by Category : Artificial Inteligence &lt;/h5&gt;

&lt;div class=&quot;card&quot;&gt;
  
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;02 Feb 2026&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;/blog/2026-02-03-cost-of-ai/&quot;&gt;The Cost of Generative AI&lt;/a&gt;&lt;/li&gt;
  
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;29 Jul 2024&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;/blog/2024-07-30-applied-data-science-mit-program/&quot;&gt;Applied Data Science Program: Levaraging AI for Effective Decision Making&lt;/a&gt;&lt;/li&gt;
  
&lt;/div&gt;
</description>
            <link>https://giofontana.github.io/blog/categories/Artificial%20Inteligence/</link>
          </item>
        
      
    
      
        
          <item>
            <title>AWS</title>
            <description>&lt;h5&gt; Posts by Category : AWS &lt;/h5&gt;

&lt;div class=&quot;card&quot;&gt;
  
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;30 Aug 2022&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;/blog/2022-08-31-rosa-transit-gateway/&quot;&gt;Deploying a Private OpenShift Cluster on AWS using Transit Gateway&lt;/a&gt;&lt;/li&gt;
  
&lt;/div&gt;
</description>
            <link>https://giofontana.github.io/blog/categories/AWS/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Cloud</title>
            <description>&lt;h5&gt; Posts by Category : Cloud &lt;/h5&gt;

&lt;div class=&quot;card&quot;&gt;
  
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;13 Dec 2020&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;/blog/2020-12-14-multicloud-journey/&quot;&gt;The start of the business on the multicloud journey&lt;/a&gt;&lt;/li&gt;
  
&lt;/div&gt;
</description>
            <link>https://giofontana.github.io/blog/categories/Cloud/</link>
          </item>
        
      
    
      
        
          <item>
            <title>CloudForms</title>
            <description>&lt;h5&gt; Posts by Category : CloudForms &lt;/h5&gt;

&lt;div class=&quot;card&quot;&gt;
  
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;22 Feb 2018&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;/blog/2018-02-23-cloudforms-database-ha/&quot;&gt;CloudForms Database High Availability Explained&lt;/a&gt;&lt;/li&gt;
  
&lt;/div&gt;
</description>
            <link>https://giofontana.github.io/blog/categories/CloudForms/</link>
          </item>
        
      
    
      
    
      
    
      
        
          <item>
            <title>Generative AI</title>
            <description>&lt;h5&gt; Posts by Category : {{ page.title }} &lt;/h5&gt;

{% assign posts = site.categories[page.title] %}
&lt;div class=&quot;card&quot;&gt;
  {% for post in posts %}
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;{{ post.date | date_to_string }}&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;{{ post.url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/li&gt;
  {% endfor %}
&lt;/div&gt;</description>
            <link>https://giofontana.github.io/blog/categories/Generative%20AI/</link>
          </item>
        
      
    
      
        
          <item>
            <title>GitOps</title>
            <description>&lt;h5&gt; Posts by Category : {{ page.title }} &lt;/h5&gt;

{% assign posts = site.categories[page.title] %}
&lt;div class=&quot;card&quot;&gt;
  {% for post in posts %}
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;{{ post.date | date_to_string }}&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;{{ post.url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/li&gt;
  {% endfor %}
&lt;/div&gt;</description>
            <link>https://giofontana.github.io/blog/categories/GitOps/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Guides</title>
            <description>&lt;h5&gt; Posts by Category : {{ page.title }} &lt;/h5&gt;

{% assign posts = site.categories[page.title] %}
&lt;div class=&quot;card&quot;&gt;
  {% for post in posts %}
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;{{ post.date | date_to_string }}&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;{{ post.url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/li&gt;
  {% endfor %}
&lt;/div&gt;</description>
            <link>https://giofontana.github.io/blog/categories/guides/</link>
          </item>
        
      
    
      
    
      
    
      
    
      
        
          <item>
            <title>Get Started</title>
            <description>## Getting Started - How to use “devlopr-jekyll” theme

## What&apos;s Jekyll ?

If you aren’t familiar with Jekyll yet, you should know that it is a static site generator. It will transform your plain text into static websites and blogs. No more databases, slow loading websites, risk of being hacked…just your content. And not only that, with Jekyll you get free hosting with GitHub Pages! If you are a beginner we recommend you start with [Jekyll’s Docs](https://jekyllrb.com/docs/installation/). Now, if you know how to use Jekyll, let’s move on to using this theme in Jekyll:

## Watch Tutorial

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/cXBEfpn0qrg?rel=0&amp;amp;controls=0&amp;amp;showinfo=0&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;


### Steps to create your blog using devlopr-jekyll and Host using Github Pages :

&gt;  **Step 1.**  Fork the repo - [click here](https://github.com/sujaykundu777/devlopr-jekyll/fork)

![Devlopr Jekyll Repo](/assets/img/posts/fork1.PNG){:class=&quot;img-fluid&quot;}

&gt; **Step 2.** Use **your-github-username.github.io** as the new repo  ( Replace your-github-username with yours). Remember if you use the name other than your-github-username.github.io , your blog will be built using gh-pages branch.

![Devlopr Jekyll Repo](/assets/img/posts/fork2.PNG){:class=&quot;img-fluid&quot;}

![Devlopr Jekyll Repo](/assets/img/posts/fork3.PNG){:class=&quot;img-fluid&quot;}

&gt; **Step 3.** Clone the new repo locally to make changes :

![Devlopr Jekyll Repo](/assets/img/posts/fork31.PNG){:class=&quot;img-fluid&quot;}

![Devlopr Jekyll Repo](/assets/img/posts/fork32.PNG){:class=&quot;img-fluid&quot;}

![Devlopr Jekyll Repo](/assets/img/posts/fork33.PNG){:class=&quot;img-fluid&quot;}

```bash
 $ git clone https://github.com/yourusername/yourusername.github.io
 $ cd yourusername.github.io
 $ code .
```

&gt; **Step 4.** Open the files using VSCode and edit _config.yml and edit with your details:

- _config.yml file - replace with your own details
- _posts - Add your blog posts here
- _includes - You can replace the contents of the files with your data. (contains widgets)
- _assets/img - Add all your images here

![Devlopr Jekyll Repo](/assets/img/posts/fork34.PNG){:class=&quot;img-fluid&quot;}

&gt; **Step 5** - Install the development requirements:

### Set up local development environment

1. [Git](https://git-scm.com/)
2. [Ruby](https://www.ruby-lang.org/) and [Bundler](https://bundler.io/)
3. [VSCode](https://code.visualstudio.com/download)

We need ruby and bundler to build our site locally. After installation check if its working:

For ruby :

```bash
$ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]
```
For bundler :

```bash
$ gem install bundler
$ bundler -v
Bundler version 2.2.29
```
Add jekyll :

```bash
$ bundle update
$ bundle add jekyll
```
 This command will add the Jekyll gem to our Gemfile and install it to the ./vendor/bundle/ folder.

You can check the jekyll version

```
$ bundle exec jekyll -v
jekyll 4.2.0
```

&gt; **Step 6.** Install the gem dependencies by running the following command

```bash
$ bundle update
$ bundle install
```

&gt; **Step 7.** Serve the site locally by running the following command below:

```bash
$ bundle exec jekyll serve --watch
```
or you can also serve using :

```bash
$ jekyll serve
```

Visit [http://localhost:4000](http://localhost:4000) for development server

![Devlopr Jekyll Repo](/assets/img/posts/fork41.PNG){:class=&quot;img-fluid&quot;}


### Adding Content

Start populating your blog by adding your .md files in _posts. devlopr-jekyll already has a few examples.

#### YAML Post Example:

```yml
---
layout: post
title: Sample Post
author: Sujay Kundu
date: &apos;2019-05-21 14:35:23 +0530&apos;
category:
        - jekyll
summary: This is the summary for the sample post
thumbnail: sample.png
---

Hi ! This is sample post.

```

#### YAML Page Example:

```yml
---
layout: page
title: Sample Page
permalink: /sample-page/
---

Hi ! This is sample page.
```

#### Editing stylesheet

You’ll only work with a single file to edit/add theme style: assets/css/main.scss.

### Deploy your Changes

Once happy with your blog changes. Push your changes to master branch.

&gt; **Step 8.** Push Your Local Changes

```bash
 $ git add .
 $ git commit -m &quot;my new blog using devlopr-jekyll&quot;
 $ git push origin master
```

Visit your Github Repo settings ! Enable master branch as Github Pages Branch :

![Devlopr Jekyll Repo](/assets/img/posts/fork6.PNG){:class=&quot;img-fluid&quot;}

&gt; **Step 9.** Deploy your Blog :

![Devlopr Jekyll Repo](/assets/img/posts/fork7.PNG){:class=&quot;img-fluid&quot;}

&gt; Congrats ! On your new shining Blog !

You can visit the blog using [http://your-github-username.github.io](http://your-github-username.github.io).

</description>
            <link>https://giofontana.github.io/get-started/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Jekyll</title>
            <description>&lt;h5&gt; Posts by Category : {{ page.title }} &lt;/h5&gt;

{% assign posts = site.categories[page.title] %}
&lt;div class=&quot;card&quot;&gt;
  {% for post in posts %}
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;{{ post.date | date_to_string }}&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;{{ post.url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/li&gt;
  {% endfor %}
&lt;/div&gt;</description>
            <link>https://giofontana.github.io/blog/categories/jekyll/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Kubevirt</title>
            <description>&lt;h5&gt; Posts by Category : {{ page.title }} &lt;/h5&gt;

{% assign posts = site.categories[page.title] %}
&lt;div class=&quot;card&quot;&gt;
  {% for post in posts %}
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;{{ post.date | date_to_string }}&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;{{ post.url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/li&gt;
  {% endfor %}
&lt;/div&gt;</description>
            <link>https://giofontana.github.io/blog/categories/Kubevirt/</link>
          </item>
        
      
    
      
    
      
    
      
        
          <item>
            <title>My Life</title>
            <description>&lt;h5&gt; Posts by Category : {{ page.title }} &lt;/h5&gt;

{% assign posts = site.categories[page.title] %}
&lt;div class=&quot;card&quot;&gt;
  {% for post in posts %}
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;{{ post.date | date_to_string }}&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;{{ post.url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/li&gt;
  {% endfor %}
&lt;/div&gt;</description>
            <link>https://giofontana.github.io/blog/categories/My Life/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Observability</title>
            <description>&lt;h5&gt; Posts by Category : {{ page.title }} &lt;/h5&gt;

{% assign posts = site.categories[page.title] %}
&lt;div class=&quot;card&quot;&gt;
  {% for post in posts %}
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;{{ post.date | date_to_string }}&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;{{ post.url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/li&gt;
  {% endfor %}
&lt;/div&gt;</description>
            <link>https://giofontana.github.io/blog/categories/Observability/</link>
          </item>
        
      
    
      
        
          <item>
            <title>OpenShift</title>
            <description>&lt;h5&gt; Posts by Category : {{ page.title }} &lt;/h5&gt;

{% assign posts = site.categories[page.title] %}
&lt;div class=&quot;card&quot;&gt;
  {% for post in posts %}
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;{{ post.date | date_to_string }}&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;{{ post.url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/li&gt;
  {% endfor %}
&lt;/div&gt;</description>
            <link>https://giofontana.github.io/blog/categories/OpenShift/</link>
          </item>
        
      
    
      
        
          <item>
            <title>OpenTelemetry</title>
            <description>&lt;h5&gt; Posts by Category : {{ page.title }} &lt;/h5&gt;

{% assign posts = site.categories[page.title] %}
&lt;div class=&quot;card&quot;&gt;
  {% for post in posts %}
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;{{ post.date | date_to_string }}&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;{{ post.url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/li&gt;
  {% endfor %}
&lt;/div&gt;</description>
            <link>https://giofontana.github.io/blog/categories/OpenTelemetry/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Packer</title>
            <description>&lt;h5&gt; Posts by Category : {{ page.title }} &lt;/h5&gt;

{% assign posts = site.categories[page.title] %}
&lt;div class=&quot;card&quot;&gt;
  {% for post in posts %}
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;{{ post.date | date_to_string }}&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;{{ post.url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/li&gt;
  {% endfor %}
&lt;/div&gt;</description>
            <link>https://giofontana.github.io/blog/categories/Packer/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Guides</title>
            <description>&lt;h5&gt; Posts by Category : {{ page.title }} &lt;/h5&gt;

{% assign posts = site.categories[page.title] %}
&lt;div class=&quot;card&quot;&gt;
  {% for post in posts %}
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;{{ post.date | date_to_string }}&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;{{ post.url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/li&gt;
  {% endfor %}
&lt;/div&gt;</description>
            <link>https://giofontana.github.io/blog/categories/sample_category/</link>
          </item>
        
      
    
      
    
      
        
          <item>
            <title>Our Sponsors</title>
            <description>Thanks to all the amazing contributors and our Backers for the support.

- [Dirish Mohan](https://dirishmohan.com)</description>
            <link>https://giofontana.github.io/sponsors/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Styleguide</title>
            <description>### devlopr - Styleguide

&lt;hr /&gt;

 &lt;img src=&quot;/assets/img/styleguide.png&quot; class=&quot;img-fluid&quot;&gt;

&lt;p&gt; Lets try the different text styles  &lt;b&gt; Bold &lt;/b&gt; , &lt;strong&gt; Strong &lt;/strong&gt;, &lt;em&gt; Emphasis &lt;/em&gt;, &lt;i&gt; Italic &lt;/i&gt; &lt;/p&gt;


&lt;p&gt; Now, lets try different heading styles : &lt;/p&gt;

&lt;h1&gt; Hello in h1 ! &lt;/h1&gt;
&lt;h2&gt; Hello in h2 ! &lt;/h2&gt;
&lt;h3&gt; Hello in h3 ! &lt;/h3&gt;
&lt;h4&gt; Hello in h4 ! &lt;/h4&gt;
&lt;h5&gt; Hello in h5 ! &lt;/h5&gt;
&lt;h6&gt; Hello in h6 ! &lt;/h6&gt;

&lt;hr /&gt;
&lt;p&gt; Unordered List &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; List Item 1 &lt;/li&gt;
&lt;li&gt; List Item 2 &lt;/li&gt;
&lt;li&gt; List Item 3 &lt;/li&gt;
&lt;li&gt; List Item 4 &lt;/li&gt;
&lt;li&gt; List Item 5 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; Ordered List &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt; List Item 1 &lt;/li&gt;
&lt;li&gt; List Item 2 &lt;/li&gt;
&lt;li&gt; List Item 3 &lt;/li&gt;
&lt;li&gt; List Item 4 &lt;/li&gt;
&lt;li&gt; List Item 5 &lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a Block Quote,  It can Expand Multiple Lines &lt;/p&gt;

&lt;/blockquote&gt;

&lt;p&gt;You can use the mark tag to &lt;mark&gt;highlight&lt;/mark&gt; text. &lt;/p&gt;

&lt;p&gt;&lt;del&gt; This line of text is meant to be deleted text &lt;/del&gt; &lt;/p&gt;

&lt;p&gt;&lt;u&gt;This line of text will render as underlined&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;small&gt;This line of text is meant to be treated as fine print.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This line rendered as bold text.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This line rendered as italicized text.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;abbr title=&quot;attribute&quot;&gt;attr&lt;/abbr&gt;&lt;/p&gt;
&lt;p&gt;&lt;abbr title=&quot;HyperText Markup Language&quot; class=&quot;initialism&quot;&gt;HTML&lt;/abbr&gt;&lt;/p&gt;

&lt;hr /&gt;
&lt;div class=&quot;responsive-table&quot;&gt;
&lt;table&gt;
      &lt;thead&gt;
        &lt;tr&gt;
          &lt;th scope=&quot;col&quot;&gt;#&lt;/th&gt;
          &lt;th scope=&quot;col&quot;&gt;Heading&lt;/th&gt;
          &lt;th scope=&quot;col&quot;&gt;Heading&lt;/th&gt;
          &lt;th scope=&quot;col&quot;&gt;Heading&lt;/th&gt;
          &lt;th scope=&quot;col&quot;&gt;Heading&lt;/th&gt;
          &lt;th scope=&quot;col&quot;&gt;Heading&lt;/th&gt;
          &lt;th scope=&quot;col&quot;&gt;Heading&lt;/th&gt;
          &lt;th scope=&quot;col&quot;&gt;Heading&lt;/th&gt;
          &lt;th scope=&quot;col&quot;&gt;Heading&lt;/th&gt;
          &lt;th scope=&quot;col&quot;&gt;Heading&lt;/th&gt;
        &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;th scope=&quot;row&quot;&gt;1&lt;/th&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;th scope=&quot;row&quot;&gt;2&lt;/th&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;th scope=&quot;row&quot;&gt;3&lt;/th&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
          &lt;td&gt;Cell&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
    &lt;/div&gt;

&lt;hr /&gt;

&lt;h3&gt; Instagram Embed &lt;/h3&gt;

&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned data-instgrm-permalink=&quot;https://www.instagram.com/p/CBXO7AypXkM/?utm_source=ig_embed&amp;amp;utm_campaign=loading&quot; data-instgrm-version=&quot;13&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:16px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/CBXO7AypXkM/?utm_source=ig_embed&amp;amp;utm_campaign=loading&quot; style=&quot; background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;&quot; target=&quot;_blank&quot;&gt; &lt;div style=&quot; display: flex; flex-direction: row; align-items: center;&quot;&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; flex-grow: 1; justify-content: center;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 19% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display:block; height:50px; margin:0 auto 12px; width:50px;&quot;&gt;&lt;svg width=&quot;50px&quot; height=&quot;50px&quot; viewBox=&quot;0 0 60 60&quot; version=&quot;1.1&quot; xmlns=&quot;https://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;https://www.w3.org/1999/xlink&quot;&gt;&lt;g stroke=&quot;none&quot; stroke-width=&quot;1&quot; fill=&quot;none&quot; fill-rule=&quot;evenodd&quot;&gt;&lt;g transform=&quot;translate(-511.000000, -20.000000)&quot; fill=&quot;#000000&quot;&gt;&lt;g&gt;&lt;path d=&quot;M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div style=&quot;padding-top: 8px;&quot;&gt; &lt;div style=&quot; color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;&quot;&gt; View this post on Instagram&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;padding: 12.5% 0;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;&quot;&gt;&lt;div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot;background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 8px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: auto;&quot;&gt; &lt;div style=&quot; width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);&quot;&gt;&lt;/div&gt; &lt;div style=&quot; width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;div style=&quot;display: flex; flex-direction: column; flex-grow: 1; justify-content: center; margin-bottom: 24px;&quot;&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 224px;&quot;&gt;&lt;/div&gt; &lt;div style=&quot; background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 144px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;&lt;a href=&quot;https://www.instagram.com/p/CBXO7AypXkM/?utm_source=ig_embed&amp;amp;utm_campaign=loading&quot; style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;&quot; target=&quot;_blank&quot;&gt;A post shared by Sujay (@sujaykundu777)&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt; &lt;script async src=&quot;//www.instagram.com/embed.js&quot;&gt;&lt;/script&gt;

&lt;hr&gt;

&lt;h3&gt; Twitter Embed &lt;/h3&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;I just published “Deploying a blog using Jekyll and Github Pages with SSL certificate for Free” &lt;a href=&quot;https://t.co/B3T3IQVU93&quot;&gt;https://t.co/B3T3IQVU93&lt;/a&gt;&lt;/p&gt;&amp;mdash; Sujay Kundu (@SujayKundu777) &lt;a href=&quot;https://twitter.com/SujayKundu777/status/1012601950469160962?ref_src=twsrc%5Etfw&quot;&gt;June 29, 2018&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;hr /&gt;


&lt;h3&gt;YouTube Responsive Embed&lt;/h3&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/bBpKMH3nBzE?rel=0&amp;amp;controls=0&amp;amp;showinfo=0&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;

&lt;hr /&gt;

&lt;h3&gt;Vimeo Responsive Embed&lt;/h3&gt;

&lt;iframe src=&quot;https://player.vimeo.com/video/212114694?title=0&amp;amp;byline=0&amp;amp;portrait=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot; webkitallowfullscreen=&quot;&quot; mozallowfullscreen=&quot;&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;ted-responsive-embed&quot;&gt;TED Responsive Embed&lt;/h3&gt;

&lt;iframe src=&quot;https://embed.ted.com/talks/ted_halstead_a_climate_solution_where_all_sides_can_win&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;twitch-responsive-embed&quot;&gt;Twitch Responsive Embed&lt;/h3&gt;

&lt;iframe src=&quot;https://player.twitch.tv/?autoplay=false&amp;amp;video=v248755437&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot; scrolling=&quot;no&quot; height=&quot;378&quot; width=&quot;620&quot;&gt;&lt;/iframe&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;soundcloud-embed&quot;&gt;SoundCloud Embed&lt;/h3&gt;

&lt;iframe width=&quot;100%&quot; height=&quot;166&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; src=&quot;https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/29738591&amp;amp;color=ff5500&amp;amp;auto_play=false&amp;amp;hide_related=false&amp;amp;show_comments=true&amp;amp;show_user=true&amp;amp;show_reposts=false&quot;&gt;&lt;/iframe&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;codepen-embed&quot;&gt;CodePen Embed&lt;/h3&gt;

&lt;p data-height=&quot;265&quot; data-theme-id=&quot;light&quot; data-slug-hash=&quot;YWvpRo&quot; data-default-tab=&quot;css,result&quot; data-user=&quot;kharrop&quot; data-embed-version=&quot;2&quot; data-pen-title=&quot;Referral Form&quot; class=&quot;codepen&quot;&gt;&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://production-assets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;syntax-highlighting&quot;&gt;Syntax Highlighting&lt;/h3&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;s1&quot;&gt;&apos;use strict&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;markdown&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;markdown&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;markdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Editor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;preview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;update&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;preview&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;markdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toHTML&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;editor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You can add inline code just like this, E.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;.code { color: #fff; }&lt;/code&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nt&quot;&gt;pre&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#f4f4f4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;github-gist-embed&quot;&gt;GitHub gist Embed&lt;/h3&gt;

&lt;script src=&quot;https://gist.github.com/ahmadajmi/dbb4f713317721668bcbc39420562afc.js&quot;&gt;&lt;/script&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;input-style&quot;&gt;Input Style&lt;/h3&gt;

&lt;p&gt;&lt;input type=&quot;text&quot; placeholder=&quot;I&apos;m an input field!&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;


</description>
            <link>https://giofontana.github.io/styleguide/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Tekton</title>
            <description>&lt;h5&gt; Posts by Category : {{ page.title }} &lt;/h5&gt;

{% assign posts = site.categories[page.title] %}
&lt;div class=&quot;card&quot;&gt;
  {% for post in posts %}
    &lt;li class=&quot;category-posts&quot;&gt;&lt;span&gt;{{ post.date | date_to_string }}&lt;/span&gt; &amp;nbsp; &lt;a href=&quot;{{ post.url }}&quot;&gt;{{ post.title }}&lt;/a&gt;&lt;/li&gt;
  {% endfor %}
&lt;/div&gt;</description>
            <link>https://giofontana.github.io/blog/categories/Tekton/</link>
          </item>
        
      
    
      
    
      
        
          <item>
            <title>Security Policy</title>
            <description># Security Policy

## Supported Versions

Use this section to tell people about which versions of your project are
currently being supported with security updates.

| Version | Supported          |
| ------- | ------------------ |
| 5.1.x   | :white_check_mark: |
| 5.0.x   | :x:                |
| 4.0.x   | :white_check_mark: |
| &lt; 4.0   | :x:                |

## Reporting a Vulnerability

Use this section to tell people how to report a vulnerability.

Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.
</description>
            <link>https://giofontana.github.io/SECURITY/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Change Log</title>
            <description># Change Log

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).

**Note that references to the Font-Awesome-Pro repository refer to a GitHub
repository that is by invitation only. You will get a 404 - Not Found if you do
not have access**

---

## [5.1.0](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.1.0)  - 2018-06-20

**Minor version upgrade notice: there are some backward-incompatible changes to this release. See the
[UPGRADING.md guide](https://github.com/FortAwesome/Font-Awesome/blob/master/UPGRADING.md) for more
information.**

### Added
* New Emoji, Design, and Travel category pack
* Another group of requested and commissioned icons
* Version 4 shim for Web Fonts with CSS
* New simplified download and NPM packages
* @fortawesome/fontawesome-free and @fortawesome/fontawesome-pro NPM packages that match what&apos;s available in the CDN and .ZIP files
* Brand icons rev, nimblr, megaport, mailchimp, hornbill, wix, weebly, themeco, squarespace, aws, shopware
* API method toHtml() for converting abstract objects to HTML
* API method counter() to generate Layers Counters
* API method watch() to configure MutationObserver and watch DOM for icon changes and additions

### Changed
* Relocating sponsor data to a separate sponsors.yml
* Updated teamspeak brand icon
* No more default exports in the CommonJS/ES packages (anything installed from NPM)
* Greatly improved performance and rendering of CSS pseudo-elements with SVG and JavaScript
* Configuration of SVG with JavaScript can now be done with attributes on the script tag
* SVG with JavaScript pseudo-elements now match syntax (font-family, font-weight) of Web Fonts with CSS

### Fixed
* Tree shaking of all NPM packages by default
* Alignment of the book-open and dice-six icon
* Correcting creative-commons
* Incorrect license on the fontawesome-common-types package
* Improve ligatures that share a base name with another ligature
* Correcting solid style of the digital-tachograph icon
* Prevent duplicating classes in some scenarios with SVG with JavaScript
* Duplicate insertion of CSS when insertCss() method was called
* Missing TypeScript definitions for the free-brands-svg-icons package

---

## [5.0.13](https://github.com/FortAwesome/Font-Awesome/releases/tag/5.0.13)  - 2018-05-10

### Added
* 68 icons to Free and 165 to Pro of the most requested icons in Font Awesome

---

## [5.0.12](https://github.com/FortAwesome/Font-Awesome/releases/tag/5.0.12)  - 2018-05-03

### Added
* A long time ago in a galaxy far, far away some icons were added

### Fixed
* Renamed the r brand to r-project to prevent ligature collision with the &quot;r&quot; glyph

---

## [5.0.11](https://github.com/FortAwesome/Font-Awesome/releases/tag/5.0.11)  - 2018-05-01

### Added
* 16 new user icons
* Full set of Creative Commons symbols
* Regular style comment-dots used for v4 comment-alt in shim
* Top 6 brand icons: r, ebay, mastodon, researchgate, keybase, teamspeak

### Changed
* Revised slider icons FortAwesome/Font-Awesome#11872
* Make desktop typeface easier to find in apps that support ligature previews

### Fixed
* Remove errant XML entity from the lastfm-square icon FortAwesome/Font-Awesome#12847
* Correcting paths in cloud icons FortAwesome/Font-Awesome-Pro#920

---

## [5.0.10](https://github.com/FortAwesome/Font-Awesome/releases/tag/5.0.10)  - 2018-04-10

### Added
* New java brand icon FortAwesome/Font-Awesome#386

### Changed
* Updating depth of dna icon
* Updating pied-piper, adding pied-piper-hat

### Fixed
* Correcting path errors on readme icon FortAwesome/Font-Awesome#12754
* Light style of lamp icon FortAwesome/Font-Awesome#12725

---

## [5.0.9](https://github.com/FortAwesome/Font-Awesome/releases/tag/5.0.9)  - 2018-03-27

### Added
* New Chat icon pack and category
* New Charity icon pack and category
* New Moving icon pack and category
* New icons hands and hand-holding

### Changed
* Updated flipboard, readme, and houzz brand icon
* Making all solid icons in the medical icon pack free
* Updated hand-holding-box and hand-receiving in the Light style

### Fixed
* Missing box-sizing CSS property for fa-layers-counter

---

## [5.0.8](https://github.com/FortAwesome/Font-Awesome/releases/tag/5.0.8)  - 2018-03-01

### Fixed
* OTF font files missing ligatures for Pro styles FortAwesome/Font-Awesome#12486 FortAwesome/Font-Awesome-Pro#1034

---

## [5.0.7](https://github.com/FortAwesome/Font-Awesome/releases/tag/5.0.7)  - 2018-02-26

### Added
* New Logistics category
* New Medical category
* Individual SVG files available from the Font Awesome CDN
* Additional search terms

### Changed
* Apple brand icon update FortAwesome/Font-Awesome#12337
* Disable mutation observers with fontawesome.noAuto() is called
* License information now references https URL scheme

### Fixed
* Missing TypeScript names FortAwesome/react-fontawesome#83
* Adding categories metadata FortAwesome/Font-Awesome#12034
* TypeScript improvement for fontawesome.layer()
* Correcting a melting, wobbling, weird-looking whistle

---

## [5.0.6](https://github.com/FortAwesome/Font-Awesome/releases/tag/5.0.6)  - 2018-01-25

### Fixed
* @fortawesome/fontawesome-pro-light missing submodules

---

## [5.0.5](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.5)  - 2018-01-25

### Added
* New Sports category
* New Chess category
* Added brand icons for flipboard, php, quinscape, and hips

### Fixed
* Sass and Less mixin fa-icon() now uses ems instead of percentage
* Corrected misspelling of &quot;Alternate&quot; in category labels
* Improved TypeScript definitions for @fortawesome/fontawesome
* Server-side rendering was failing due to DOM-specific object access
* SVG attributes &quot;data-fa-processed&quot; renamed to &quot;data-fa-i2svg&quot;, only applies if rendered with i2svg() method

---

## [5.0.4](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.4)  - 2018-01-10

### Changed
* Updating all NPM package READMEs

### Fixed
* Improving TypeScript exports and fixing some incorrect definitions
* TypeScript error when importing entire style Fort-Awesome/Font-Awesome#12072
* Pseudo-elements erasing text contents in parent container Fort-Awesome/Font-Awesome-Pro#11995
* fa-layers-text misalignment when using Bootstrap Fort-Awesome/Font-Awesome#11871

---

## [5.0.3](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.3)  - 2018-01-08

### Added
* Adding elementor, youtube-square brand icons
* Adding window-minimize to the Free subset
* TypeScript support for all NPM packages

### Fixed
* Corrected uneven spacing in university, address-book, address-card, id-badge, id-card, mouse-pointer, phone-volume, portrait, user-alt, user-circle, user-md, user-plus, user-times, user , users
* Corrected uneven spacing in brand icons behance-square, dashcube, discourse, ember, erlang, fort-awesome, js-square, laravel, mix, patreon, palfed, phoenix-framework, node-js, skyatlas, stack-exchange, stripe, viber, weixin, yahoo , yoast

---

## [5.0.2](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.2)  - 2017-12-19

### Added
* Adding amazon-pay, cc-amazon-pay, korvue, ethereum brand icons
* Adding stopwatch to Free version

### Changed
* Ligatures now support capital case, all caps, and title case

### Fixed
* NPM packages now behave the same way as CDN and browser-specific packages FortAwesome/Font-Awesome-Pro#727 FortAwesome/Font-Awesome-Pro#896 FortAwesome/Font-Awesome-Pro#891
* Icon doesn&apos;t change when pseudo-element content changes FortAwesome/Font-Awesome-Pro#839
* Invalid XML in sprites FortAwesome/Font-Awesome-Pro#927
* Incorrect version in Sass and Less variable files

---

## [5.0.1](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.1)  - 2017-12-08

### Added
* Adding font-awesome-flag, lock-open, redo-alt, sync-alt, undo-alt to the Free version
* New NPM packages `fontawesome-free-webfonts` and `fontawesome-pro-webfonts`
* Adding old icon names to search terms for renamed icons
* Extensive metadata added to the `advanced-options` directory
* Adding stripe-s brand icon
* Adding typo3 brand icon

### Changed
* Updated dropbox brand icon to match new branding guidelines
* Updated firefox brand icon
* Updated strava brand icon
* OTF font file now include a space character

### Fixed
* OTF font file now supports different styles in Windows
* OTF font file &quot;j&quot; character now has correct space on the right
* Modifying the `class` attribute on an existing `&lt;svg&gt;` allows you to change the icon

---

## [5.0.0](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0)  - 2017-12-01

### Added
* License information

### Changed
* CSS vertical-align now &quot;em&quot;-based instead of percentage making it more consistent
* fa-ul width now closer to default browser size

---

## [5.0.0-rc5](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-rc5)  - 2017-11-28

**This release includes breaking changes**

### Added
* Brand icons: gitter, cc-stripe, stripe, hooli, aviato, strava, ember, angular, font-awesome-flag
* Icons compress-alt and expand-alt
* Adding calendar to Font Awesome 5 Free
* SASS function that makes it easier to use variables FortAwesome/Font-Awesome-Pro#824

### Changed
* BREAKING Renamed icon composition to mask (&quot;data-fa-compose&quot; becomes &quot;data-fa-mask&quot;)
* BREAKING Re-organized directory structure to match upcoming documentation
* BREAKING Font Awesome styles inserted into the `&lt;head&gt;` will now precede other link and style definitions
* BREAKING `fontawesome.text` and `fontawesome.icon` now use `styles` param instead of `style`
* Updated sizing for twitter, discord, youtube
* Class fa-li now respects line-height and has new recommended markup (see included docs)

### Fixed
* Duplicate `style` tags being added in the head FortAwesome/Font-Awesome-Pro#858
* Error with icon composition/masking that caused a confusing error message
* An error when using pseudo elements and the element is empty (Array.reduce error)
* Icons not being replaced with SVG if the text content is not empty

---

## [5.0.0-rc4](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-rc4)  - 2017-10-27

### Added
* Ligature support in the OTF font
* Vue.js brand icon
* Sass and Less brand icons
* Autoprefixer brand icon
* Individual icon imports in icon packages FortAwesome/Font-Awesome-Pro#808

### Changed
* Better poo eyes
* Renamed HTML status classes to `fontawesome-i2svg-active`, `fontawesome-i2svg-pending`, `fontawesome-i2svg-complete`
* HTML status class for active is added only after the first batch of icon replacements occur
* Added mention of newer versions of iOS in documentation FortAwesome/Font-Awesome-Pro#810

### Fixed
* Performance and missing features with mutation observer (should fix FortAwesome/Font-Awesome-Pro#813)
* Incorrect handling of icon class and style attributes when using autoReplace = &apos;nest&apos; FortAwesome/Font-Awesome-Pro#809
* Pseudo elements not added or removed when class mutations occur FortAwesome/Font-Awesome-Pro#821

---

## [5.0.0-rc3](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-rc3)  - 2017-10-13

### Added
* Node.js brand icon FortAwesome/Font-Awesome-Pro#779
* React brand icon FortAwesome/Font-Awesome-Pro#780
* OSI brand icon FortAwesome/Font-Awesome-Pro#748
* Add a class to the html element when icon replacement is complete FortAwesome/Font-Awesome-Pro#778
* Add support for symbols in API including ability to name the symbol
* Use CSS pseudo elements (:before and :after) to make trigger SVG replacements

### Changed
* Switched the locations of fork and knife in utensils-alt FortAwesome/Font-Awesome-Pro#466
* Updated the AWS brand icon FortAwesome/Font-Awesome-Pro#735
* Updated Apple App Store icon FortAwesome/Font-Awesome-Pro#728

### Fixed
* Do not throw an error if icon is missing when calling icon() method in API
* Ensure that unicode values do not change between releases
* Version field is missing in fontawesome-pro-brands/package.json FortAwesome/Font-Awesome-Pro#781
* Repeated commenting out of fa-layers when i2svg is called FortAwesome/Font-Awesome-Pro#788
* Title not showing up correctly for SVG FortAwesome/Font-Awesome-Pro#786

---

## [5.0.0-rc2](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-rc2)  - 2017-09-22

### Added
* Brand icons: accusoft, ns8, uniregistry

### Fixed
* Link to the npm package in the docs FortAwesome/Font-Awesome-Pro#729
* Incorrect reference to fontawesome-pro.js in docs

---

## [5.0.0-rc1](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-rc1)  - 2017-09-15

### Changed
* New Bitbucket logo FortAwesome/Font-Awesome-Pro#720
* Modifed the star icons to match use case better FortAwesome/Font-Awesome-Pro#710
* Switched names of css3 and css3-alt to reflect correct branding

### Fixed
* Correct whitespace with the Visa logo FortAwesome/Font-Awesome-Pro#719
* Improve OTF support by passing through FontForge FortAwesome/Font-Awesome-Pro#565
* Fonts with &quot;undefined&quot; name FortAwesome/Font-Awesome-Pro#711
* Shims will only function if using old prefix of &quot;fa&quot; FortAwesome/Font-Awesome-Pro#692
* Added missing &quot;youtube&quot; icon to categories

---

## [5.0.0-beta7](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-beta7)  - 2017-09-11

### Added
* Ability to nest the `&lt;svg&gt;` tag within the `&lt;i&gt;` FortAwesome/Font-Awesome-Pro#624
* Define icons as symbols and leverage SVG sprites FortAwesome/Font-Awesome-Pro#629
* Added alternative CSS3 logo FortAwesome/Font-Awesome-Pro#682

### Changed
* Power Transforms now execute inside the SVG instead of on the root element
* Filenames have changed to reflect a better division between Font Awesome Free and Pro

### Fixed
* More improvements to the version 4 shim FortAwesome/Font-Awesome-Pro#673 FortAwesome/Font-Awesome-Pro#678 FortAwesome/Font-Awesome-Pro#686 FortAwesome/Font-Awesome-Pro#687 FortAwesome/Font-Awesome-Pro#692
* Animation support for inline SVG now works as expected FortAwesome/Font-Awesome-Pro#662

---

## [5.0.0-beta6](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-beta6)  - 2017-09-01

### Added
* Ability to flip horizontal and vertical with CSS classes fa-flip-horizontal and fa-flip-vertical
* New film-alt icon that allows for layering other icons
* Microsoft brand

### Changed
* New YouTube branding FortAwesome/Font-Awesome-Pro#646

### Fixed
* Fixed a bunch of shim-related issues
* Cogs off center FortAwesome/Font-Awesome-Pro#663
* Corrected icons/categories.yml with canonical names

---

## [5.0.0-beta5](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-beta5)  - 2017-08-25

### Added
* Full parity with Font Awesome 4! 616 total core icons in each style
* 297 total brand and logo icons
* Separate CSS file to accompany the SVG Framework FortAwesome/Font-Awesome-Pro#627
* Alternative to the dots icon FortAwesome/Font-Awesome-Pro#608
* Made window icons consistent FortAwesome/Font-Awesome-Pro#611

### Fixed
* Production builds not correctly being detected FortAwesome/Font-Awesome-Pro#631

---

## [5.0.0-beta4](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-beta4)  - 2017-08-18

### Added
* 590 total core icons in each style
* 291 total brand and logo icons

### Fixed
* Reduced the size of JS file from 66 to 22 kb
* Regression caused by with web font alignment FortAwesome/Font-Awesome-Pro#460

---

## [5.0.0-beta3](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-beta3)  - 2017-08-15

### Added
* 583 total core icons in each style

### Fixed
* Documentation improvements and fixes FortAwesome/Font-Awesome-Pro#586
* Vertical alignment of TTF and OTF fonts FortAwesome/Font-Awesome-Pro#460
* The &quot;fa_500px&quot; icon should be named &quot;fa500px&quot; FortAwesome/Font-Awesome-Pro#578

---

## [5.0.0-beta2](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-beta2)  - 2017-08-11

### Added
* 570 total core icons in each style
* 291 total brand and logo icons
* NPM (ES6, CommonJS, AMD) packages for use with other JavaScript libraries and tools FortAwesome/Font-Awesome-Pro#574
* Added a guide to choosing which implementation is best for you FortAwesome/Font-Awesome-Pro#532

### Changed
* Showing a missing icon is now configurable FortAwesome/Font-Awesome-Pro#569

### Fixed
* Composition framework now works in browsers that do not support transform-origin FortAwesome/Font-Awesome-Pro#564

---

## [5.0.0-beta1](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-beta1)  - 2017-08-04

### Added
* 524 total core icons in each style
* 289 total brand and logo icons
* New composition framework FortAwesome/Font-Awesome-Pro#537
* Animated indicator if you use an icon that does not exist

### Changed
* Basic linting for Sass and Less files
* Add JavaScript guard block to prevent leaking errors
* Add support for automatic accessibility to SVG Framework Layers

### Fixed
* Regression where stacks and pulled and bordered were not working in SVG Framework
* SVG sprite example had confusing inline styles FortAwesome/Font-Awesome-Pro#549
* Make getting started page more consistent between examples FortAwesome/Font-Awesome-Pro#544
* Added missing sizes fa-[6-10], xs, sm FortAwesome/Font-Awesome-Pro#546
* Title tag missing in SVG sprites FortAwesome/Font-Awesome-Pro#536

---

## [5.0.0-alpha7](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-alpha7)  - 2017-07-28

### Added
* 451 total core icons in each style
* 281 total brand and logo icons
* Less support is back!
* OpenType (.otf) file formats for web fonts

### Changed
* Changes the fa-spin animation to go from 0deg to 360deg to eliminate hitch FortAwesome/Font-Awesome-Pro#522
* Improved mutation handling FortAwesome/Font-Awesome-Pro#517

### Fixed
* fa-fw now works correctly with the SVG framework FortAwesome/Font-Awesome-Pro#530
* Removed execute bit on some icon files FortAwesome/Font-Awesome-Pro#520

---

## [5.0.0-alpha6](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-alpha6)  - 2017-07-21

### Added
* 410 total core icons in each style
* 270 total brand and logo icons
* All new Font Awesome 4 shim file
* Beginnings of a public JS API FortAwesome/Font-Awesome-Pro#512

### Changed
* Added Firefox ESR and Chrome for Businesses to browser compatibility FortAwesome/Font-Awesome-Pro#506

### Fixed
* Ensure that SVG title attributes are unique
* Fixed incorrect viewBox sizes FortAwesome/Font-Awesome-Pro#492
* Fix chart-area alignment in the solid style FortAwesome/Font-Awesome-Pro#508
* Add missing xmlns attributes in some SVGs FortAwesome/Font-Awesome-Pro#509

---

## [5.0.0-alpha5](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-alpha5)  - 2017-07-14

### Added
* 228 total brand and logo icons
* New transform framework for sizing, moving, rotating, and flipping icons
* New icon counters
* New layers framework
* New text overlays
* Auto-comments with the original source icons alongside SVG replacements

### Changed
* Autoprefixer to correctly add browser prefixes for supported browsers
* Removed browser-specific CSS properties in Sass source files (now relies on autoprefixer)

### Fixed
* The rotation on checkmark icons
* Other icon feedback from previous weeks
* Correct fixed width settings to 1.25em (based on the new 16px grid)
* Icons displaying as block instead of inline-block in IE and older Safari

---

## [5.0.0-alpha4](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-alpha4)  - 2017-07-07

### Added
* 93 brand icons

---

## [5.0.0-alpha3](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-alpha3)  - 2017-06-30

### Added
* 95 additional icons; including file types, directional, and some existing and new brand icons

### Fixed
* Wrong content type in generated CSS FortAwesome/Font-Awesome-Pro#458
* Removal of query string from static resources FortAwesome/Font-Awesome-Pro#458
* SVG font ID&apos;s are incorrect in webfont implementation FortAwesome/Font-Awesome-Pro#474

---

## [5.0.0-alpha2](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-alpha2)  - 2017-06-27

### Added
* How/When to upgrade from FA4 to FA5 FortAwesome/Font-Awesome-Pro#454

### Fixed
* Links to SVG files broken in the example files FortAwesome/Font-Awesome-Pro#456
* Misnamed icon names in examples FortAwesome/Font-Awesome-Pro#445
* Mangled HTML in the Getting Started example FortAwesome/Font-Awesome-Pro#442
* Bad grammar and typos FortAwesome/Font-Awesome-Pro#443
* fas-arrow-to-top is identical to fas-arrow-to-right FortAwesome/Font-Awesome-Pro#423
* Vertical alignment issues with webfont implementation FortAwesome/Font-Awesome-Pro#444
* Add browser compatibility tables to demo FortAwesome/Font-Awesome-Pro#435
* Remove MAC OS feces from builds FortAwesome/Font-Awesome-Pro#437
* TTF naming issues that prevent correct usage/installation FortAwesome/Font-Awesome-Pro#450
* Correct CSS for SVG framework stacking, was reversed from normal FortAwesome/Font-Awesome-Pro#452

---

## [5.0.0-alpha1](https://github.com/FortAwesome/Font-Awesome-Pro/releases/tag/5.0.0-alpha1)  - 2017-06-23

### Added
* 300+ more icons
* Brands pack
* New JavaScript based SVG Framework
* New SVG Sprites based framework
* Source SVGs
* Documentation with a convenient build-in web server

### Changed
* New directory structure
</description>
            <link>https://giofontana.github.io/assets/bower_components/font-awesome/CHANGELOG/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Upgrading Guide</title>
            <description># Upgrading Guide

See the [CHANGELOG.md](/assets/bower_components/font-awesome/CHANGELOG/) for detailed information about what has changed between versions.

This guide is useful to figure out what you need to do between breaking changes.

As always, [submit issues](https://github.com/FortAwesome/Font-Awesome/issues/new) that you run into with this guide or with these upgrades to us.

## 5.0.x to 5.1.0

### New packages available for browser-only integration

**If you were previously using @fortawesome/fontawesome you need to switch to one of the new packages.**

Our Free and Pro CDN provide access to JS, CSS, sprites, and separate SVG files.

We&apos;ve now made these files conveniently available through NPM.

* [@fortawesome/fontawesome-free](https://www.npmjs.com/package/@fortawesome/fontawesome-free)
* @fortawesome/fontawesome-pro (private package, requires Pro subscription)

If you are familiar with the paths and options available with the CDN these
packages should be familiar.

Information about [Font Awesome Pro subscriptions](https://fontawesome.com/pro)
can be found in your [Font Awesome awesome
account](https://fontawesome.com/account/services).

### Renamed packages

The following packages have been renamed as part of 5.1.0 of Font Awesome.

_All packages are in the [@fortawesome NPM scope](https://www.npmjs.com/search?q=scope:fortawesome&amp;page=1&amp;ranking=optimal)_

| Old package(1)            | New package            |
|---------------------------|------------------------|
| fontawesome-free-webfonts | fontawesome-free       |
| fontawesome-pro-webfonts  | fontawesome-pro        |
| fontawesome-free-solid    | free-solid-svg-icons   |
| fontawesome-free-regular  | free-regular-svg-icons |
| fontawesome-free-brands   | free-brands-svg-icons  |
| fontawesome-pro-solid     | pro-solid-svg-icons    |
| fontawesome-pro-regular   | pro-regular-svg-icons  |
| fontawesome-pro-light     | pro-light-svg-icons    |

(1) Old packages have now been deprecated. They are still available but will only receive high priority patch release fixes.

**You&apos;ll need to update your package.json file with the renamed packages and new versions.**

### No more default imports

Recently we spent a good deal of time supporting TypeScript to enable us to
create the Angular Font Awesome component. During that adventure we
[were](https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html)
[convinced](https://blog.neufund.org/why-we-have-banned-default-exports-and-you-should-do-the-same-d51fdc2cf2ad)
that we were going to remove default exports from all of our components,
libraries, and packages. This is complete with the umbrella release of `5.1.0` of Font Awesome.

What does that mean?

~~Old way:~~

```javascript
import fontawesome from &apos;@fortawesome/fontawesome&apos;
import solid from &apos;@fortawesome/fontawesome-free-solid&apos;
import faTwitter from &apos;@fortawesome/fontawesome-free-brands/faTwitter&apos;
import FontAwesomeIcon from &apos;@fortawesome/vue-fontawesome&apos;

library.add(solid, faTwitter)
```

New way:

```javascript
import { library, dom } from &apos;@fortawesome/fontawesome-svg-core&apos;
import { fas } from &apos;@fortawesome/free-solid-svg-icons&apos;
import { faTwitter } from &apos;@fortawesome/free-brands-svg-icons&apos;
import { FontAwesomeIcon } from &apos;@fortawesome/vue-fontawesome&apos;

library.add(fas, faTwitter)

// Kicks off the process of finding &lt;i&gt; tags and replacing with &lt;svg&gt;
dom.watch()
```

This is also a valid way to import icons that works if your tool does not support tree shaking:

```javascript
import { faTwitter } from &apos;@fortawesome/free-brands-svg-icons/faTwitter&apos;
```

### Improved support for tree shaking

Tree shaking is now functional by default and no additional configuration is required to make it work.

The `shakable.es.js` module has been removed and is no longer needed.

If you&apos;ve previously configured tree shaking by modifying your webpack or rollup you can safely remove these.

**We recommend that you check your bundle size after upgrading an ensure that file sizes are as you would expect.**

```javascript
module.exports = {
  // ...
  resolve: {
    alias: {
      &apos;@fortawesome/fontawesome-free-solid$&apos;: &apos;@fortawesome/fontawesome-free-solid/shakable.es.js&apos;
    }
  }
}
```

```javascript
const alias = require(&apos;rollup-plugin-alias&apos;)

rollup({
  // ...
  plugins: [
    alias({
      &apos;@fortawesome/fontawesome-free-solid&apos;: &apos;node_modules/@fortawesome/fontawesome-free-solid/shakable.es.js&apos;
    })
  ]
})
```

## 5.0.11 to 5.0.12

Due to a collision with the &quot;r&quot; glyph the R Project brand icon has been renamed to `r-project`.

## 5.0.x to 5.0.6

### SVG Attribute was changed from data-fa-processed to data-fa-i2svg

As part of a bug fix for the release of 5.0.6 we renamed an attribute that was found on `&lt;svg&gt;` elements from
`data-fa-processed` to `data-fa-i2svg`. We feel this more accurately reflects the intent and purpose.

This attribute is added to any icon that has been generated using `fontawesome.dom.i2svg()`.

Be aware that `data-fa-i2svg` (or `data-fa-processed`) will no longer be present on icons that are created using
`fontawesome.icon()`.

If you&apos;ve written and DOM queries that rely on `data-fa-processed` you should get things working again by doing a
simple find and replace.
</description>
            <link>https://giofontana.github.io/assets/bower_components/font-awesome/UPGRADING/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Mouse Wheel ChangeLog</title>
            <description># Mouse Wheel ChangeLog

## 3.1.13

* Update copyright notice and license to remove years
* Create the correct compressed version
* Remove the obsolete jQuery Plugin Registry file

## 3.1.12

* Fix possible 0 value for line height when in delta mode 1

## 3.1.11

* Fix version number for package managers...

## 3.1.10

* Fix issue with calculating line height when using older versions of jQuery
* Add offsetX/Y normalization with setting to turn it off
* Cleans up data on teardown

## 3.1.9

* Fix bower.json file
* Updated how the deltas are adjusted for older mousewheel based events that have deltas that are factors of 120.
* Add $.event.special.mousewheel.settings.adjustOldDeltas (defaults to true) to turn off adjusting of old deltas that are factors of 120. You&apos;d turn this off if you want to be as close to native scrolling as possible.

## 3.1.8

* Even better handling of older browsers that use a wheelDelta based on 120
* And fix version reported by `$.event.special.mousewheel`

## 3.1.7

* Better handle the `deltaMode` values 1 (lines) and 2 (pages)
* Attempt to better handle older browsers that use a wheelDelta based on 120

## 3.1.6

* Deprecating `delta`, `deltaX`, and `deltaY` event handler arguments
* Update actual event object with normalized `deltaX `and `deltaY` values (`event.deltaX`, `event.deltaY`)
* Add `deltaFactor` to the event object (`event.deltaFactor`)
* Handle `&gt; 0` but `&lt; 1` deltas better
* Do not fire the event if `deltaX` and `deltaY` are `0`
* Better handle different devices that give different `lowestDelta` values
* Add `$.event.special.mousewheel.version`
* Some clean up

## 3.1.5

* Bad release because I did not update the new `$.event.special.mousewheel.version`

## 3.1.4

* Always set the `deltaY`
* Add back in the `deltaX` and `deltaY` support for older Firefox versions

## 3.1.3

* Include `MozMousePixelScroll` in the to fix list to avoid inconsistent behavior in older Firefox

## 3.1.2

* Include grunt utilities for development purposes (jshint and uglify)
* Include support for browserify
* Some basic cleaning up

## 3.1.1

* Fix rounding issue with deltas less than zero


## 3.1.0

* Fix Firefox 17+ issues by using new wheel event
* Normalize delta values
* Adds horizontal support for IE 9+ by using new wheel event
* Support AMD loaders


## 3.0.6

* Fix issue with delta being 0 in Firefox


## 3.0.5

* jQuery 1.7 compatibility


## 3.0.4

* Fix IE issue


## 3.0.3

* Added `deltaX` and `deltaY` for horizontal scrolling support (Thanks to Seamus Leahy)


## 3.0.2

* Fixed delta being opposite value in latest Opera
* No longer fix `pageX`, `pageY` for older Mozilla browsers
* Removed browser detection
* Cleaned up the code


## 3.0.1

* Bad release... creating a new release due to plugins.jquery.com issue :(


## 3.0

* Uses new special events API in jQuery 1.2.2+
* You can now treat `mousewheel` as a normal event and use `.bind`, `.unbind` and `.trigger`
* Using jQuery.data API for expandos


## 2.2

* Fixed `pageX`, `pageY`, `clientX` and `clientY` event properties for Mozilla based browsers


## 2.1.1

* Updated to work with jQuery 1.1.3
* Used one instead of bind to do unload event for clean up


## 2.1

* Fixed an issue with the unload handler


## 2.0

* Major reduction in code size and complexity (internals have change a whole lot)


## 1.0

* Fixed Opera issue
* Fixed an issue with children elements that also have a mousewheel handler
* Added ability to handle multiple handlers
</description>
            <link>https://giofontana.github.io/assets/bower_components/jquery-mousewheel/ChangeLog/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Fizzy UI utils</title>
            <description># Fizzy UI utils

UI utility &amp; helper functions

Used in [Flickity](http://flickity.metafizzy.co), [Isotope](http://isotope.metafizzy.co), [Masonry](http://masonry.desandro.com), [Draggabilly](http://draggabilly.desandro.com)

## Install

Bower: `bower install fizzy-ui-utils --save`

npm: `npm install fizzy-ui-utils --save`

## API

``` js
// fizzyUIUtils is the browser global
var utils = fizzyUIUtils;

// ---- ---- //

utils.extend( a, b )
// extend object

utils.modulo( num, div )
// num [modulo] div

utils.makeArray( obj )
// make array from object

utils.removeFrom( ary, obj )
// remove object from array

utils.getParent( elem, selector )
// get parent element of an element, given a selector string

utils.getQueryElement( elem )
// if elem is a string, use it as a selector and return element

Class.prototype.handleEvent = utils.handleEvent;
// enable Class.onclick when element.addEventListener( &apos;click&apos;, this, false )

utils.filterFindElements( elems, selector )
// iterate through elems, filter and find all elements that match selector

utils.debounceMethod( Class, methodName, threhold )
// debounce a class method

utils.docReady( callback )
// trigger callback on document ready

utils.toDashed( str )
// &apos;camelCaseString&apos; -&gt; &apos;camel-case-string&apos;

utils.htmlInit( Class, namespace )
// on document ready, initialize Class on every element
// that matches js-namespace
// pass in JSON options from element&apos;s data-options-namespace attribute
```

---

[MIT license](http://desandro.mit-license.org/). Have at it.

By [Metafizzy](http://metafizzy.co)
</description>
            <link>https://giofontana.github.io/assets/bower_components/fizzy-ui-utils/</link>
          </item>
        
      
    
      
        
          <item>
            <title>@fortawesome/free-regular-svg-icons - SVG with JavaScript version</title>
            <description># @fortawesome/free-regular-svg-icons - SVG with JavaScript version

&gt; &quot;I came here to chew bubblegum and install Font Awesome 5 - and I&apos;m all out of bubblegum&quot;

[![npm](https://img.shields.io/npm/v/@fortawesome/free-regular-svg-icons.svg?style=flat-square)](https://www.npmjs.com/package/@fortawesome/free-regular-svg-icons)

## Installation

```
$ npm i --save @fortawesome/free-regular-svg-icons
```

Or

```
$ yarn add @fortawesome/free-regular-svg-icons
```

## Documentation

Get started [here](https://fontawesome.com/get-started/svg-with-js). Continue your journey [here](https://fontawesome.com/how-to-use/svg-with-js).

Or go straight to the [API documentation](https://fontawesome.com/how-to-use/font-awesome-api).

## Issues and support

Start with [GitHub issues](https://github.com/FortAwesome/Font-Awesome/issues) and ping us on [Twitter](https://twitter.com/fontawesome) if you need to.
</description>
            <link>https://giofontana.github.io/assets/bower_components/font-awesome/advanced-options/use-with-node-js/free-regular-svg-icons/</link>
          </item>
        
      
    
      
        
          <item>
            <title>jQuery Easing Plugin</title>
            <description># jQuery Easing Plugin

What is it? A jQuery plugin from GSGD to give advanced easing options. More info [here](http://gsgd.co.uk/sandbox/jquery/easing)

For CDN please use CloudFlare [`https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js`](https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js) to help my host. Thank you.

# AMD or CommonJS usage

```js
// CommonJS
var jQuery = require(&apos;jquery&apos;);
require(&apos;jquery.easing&apos;)(jQuery);

// AMD
define([&apos;jquery&apos;, &apos;jquery.easing&apos;], function (jQuery, easing) {
  easing(jQuery)
})
```

# Building and testing

* Clone the repo
* `npm install`
* Make changes
* Test against files in `/examples`
* Build minified version with `npm run build`
</description>
            <link>https://giofontana.github.io/assets/bower_components/jquery.easing/</link>
          </item>
        
      
    
      
        
          <item>
            <title>jQuery</title>
            <description># jQuery

&gt; jQuery is a fast, small, and feature-rich JavaScript library.

For information on how to get started and how to use jQuery, please see [jQuery&apos;s documentation](http://api.jquery.com/).
For source files and issues, please visit the [jQuery repo](https://github.com/jquery/jquery).

If upgrading, please see the [blog post for 3.3.1](https://blog.jquery.com/2017/03/20/jquery-3.3.1-now-available/). This includes notable differences from the previous version and a more readable changelog.

## Including jQuery

Below are some of the most common ways to include jQuery.

### Browser

#### Script tag

```html
&lt;script src=&quot;https://code.jquery.com/jquery-3.3.1.min.js&quot;&gt;&lt;/script&gt;
```

#### Babel

[Babel](http://babeljs.io/) is a next generation JavaScript compiler. One of the features is the ability to use ES6/ES2015 modules now, even though browsers do not yet support this feature natively.

```js
import $ from &quot;jquery&quot;;
```

#### Browserify/Webpack

There are several ways to use [Browserify](http://browserify.org/) and [Webpack](https://webpack.github.io/). For more information on using these tools, please refer to the corresponding project&apos;s documention. In the script, including jQuery will usually look like this...

```js
var $ = require(&quot;jquery&quot;);
```

#### AMD (Asynchronous Module Definition)

AMD is a module format built for the browser. For more information, we recommend [require.js&apos; documentation](http://requirejs.org/docs/whyamd.html).

```js
define([&quot;jquery&quot;], function($) {

});
```

### Node

To include jQuery in [Node](nodejs.org), first install with npm.

```sh
npm install jquery
```

For jQuery to work in Node, a window with a document is required. Since no such window exists natively in Node, one can be mocked by tools such as [jsdom](https://github.com/tmpvar/jsdom). This can be useful for testing purposes.

```js
require(&quot;jsdom&quot;).env(&quot;&quot;, function(err, window) {
	if (err) {
		console.error(err);
		return;
	}

	var $ = require(&quot;jquery&quot;)(window);
});
```
</description>
            <link>https://giofontana.github.io/assets/bower_components/jquery/</link>
          </item>
        
      
    
      
        
          <item>
            <title>Outlayer</title>
            <description># Outlayer

_Brains and guts of a layout library_

Outlayer is a base layout class for layout libraries like [Isotope](http://isotope.metafizzy.co), [Packery](http://packery.metafizzy.co), and [Masonry](http://masonry.desandro.com)

Outlayer layouts work with a container element and children item elements.

``` html
&lt;div class=&quot;grid&quot;&gt;
  &lt;div class=&quot;item&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;item&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;item&quot;&gt;&lt;/div&gt;
  ...
&lt;/div&gt;
```

## Install

Install with [Bower](http://bower.io): `bower install outlayer`

[Install with npm](http://npmjs.org/package/outlayer): `npm install outlayer`

## Outlayer.create()

Create a layout class with `Outlayer.create()`

``` js
var Layout = Outlayer.create( namespace );
// for example
var Masonry = Outlayer.create(&apos;masonry&apos;);
```

+ `namespace` _{String}_ should be camelCased
+ returns `LayoutClass` _{Function}_

Create a new layout class. `namespace` is used for jQuery plugin, and for declarative initialization.

The `Layout` inherits from [`Outlayer.prototype`](docs/outlayer.md).

```
var elem = document.querySelector(&apos;.selector&apos;);
var msnry = new Masonry( elem, {
  // set options...
  columnWidth: 200
});
```

## Item

Layouts work with Items, accessible as `Layout.Item`. See [Item API](docs/item.md).

## Declarative

An Outlayer layout class can be initialized via HTML, by setting an attribute of `data-namespace` on the element. Options are set in JSON. For example:

``` html
&lt;!-- var Masonry = Outlayer.create(&apos;masonry&apos;) --&gt;
&lt;div class=&quot;grid&quot; data-masonry=&apos;{ &quot;itemSelector&quot;: &quot;.item&quot;, &quot;columnWidth&quot;: 200 }&apos;&gt;
  ...
&lt;/div&gt;
```

The declarative attributes and class will be dashed. i.e. `Outlayer.create(&apos;myNiceLayout&apos;)` will use `data-my-nice-layout` as the attribute.

## .data()

Get a layout instance from an element.

```
var myMasonry = Masonry.data( document.querySelector(&apos;.grid&apos;) );
```

## jQuery plugin

The layout class also works as jQuery plugin.

``` js
// create Masonry layout class, namespace will be the jQuery method
var Masonry = Outlayer.create(&apos;masonry&apos;);
// rock some jQuery
$( function() {
  // .masonry() to initialize
  var $grid = $(&apos;.grid&apos;).masonry({
    // options...
  });
  // methods are available by passing a string as first parameter
  $grid.masonry( &apos;reveal&apos;, elems );
});
```

## RequireJS

To use Outlayer with [RequireJS](http://requirejs.org/), you&apos;ll need to set some config.

Set [baseUrl](http://requirejs.org/docs/api.html#config-baseUrl) to bower_components and set a [path config](http://requirejs.org/docs/api.html#config-paths) for all your application code.

``` js
requirejs.config({
  baseUrl: &apos;bower_components&apos;,
  paths: {
    app: &apos;../&apos;
  }
});

requirejs( [ &apos;outlayer/outlayer&apos;, &apos;app/my-component.js&apos; ], function( Outlayer, myComp ) {
  new Outlayer( /*...*/ )
});
```

Or set a path config for all Outlayer dependencies.

``` js
requirejs.config({
  paths: {
    &apos;ev-emitter&apos;: &apos;bower_components/ev-emitter&apos;,
    &apos;get-size&apos;: &apos;bower_components/get-size&apos;,
    &apos;matches-selector&apos;: &apos;bower_components/matches-selector&apos;
  }
});
```

## MIT license

Outlayer is released under the [MIT license](http://desandro.mit-license.org).
</description>
            <link>https://giofontana.github.io/assets/bower_components/outlayer/</link>
          </item>
        
      
    
      
    
      
    
      
    

  </channel>
</rss>