<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2026-03-24T07:22:47+00:00</updated><id>/feed.xml</id><title type="html">amedeos home</title><subtitle>I&apos;m a Linux nerd, Open{Stack,Shift} nerd, DevOps nerd, music nerd, Gentoo nerd and runner nerd in my life.</subtitle><entry><title type="html">Inspecting OpenShift container images for cgroups v2 compatibility</title><link href="/openshift/2026/03/09/Inspecting-OpenShift-container-images-for-cgroups-v2-compatibility.html" rel="alternate" type="text/html" title="Inspecting OpenShift container images for cgroups v2 compatibility" /><published>2026-03-09T07:00:00+00:00</published><updated>2026-03-09T07:00:00+00:00</updated><id>/openshift/2026/03/09/Inspecting-OpenShift-container-images-for-cgroups-v2-compatibility</id><content type="html" xml:base="/openshift/2026/03/09/Inspecting-OpenShift-container-images-for-cgroups-v2-compatibility.html"><![CDATA[<p>In my daily work I regularly help customers plan their OpenShift upgrades, and one of the most common concerns when moving to cgroups v2 is understanding which workloads are affected. I needed a quick and reliable way to scan an entire cluster and identify incompatible Java, Node.js, and .NET runtimes, so I built <strong>image-cgroupsv2-inspector</strong> and released it as open source, so that anyone can use it, modify it, and contribute to it.</p>

<p>In this article, I’ll walk you through the tool: what it does, how it works, and how you can use it on your own clusters. If you’re planning to upgrade your OpenShift cluster to a version that uses cgroups v2 by default (OpenShift 4.14+), or if you’re migrating from cgroups v1, this tool helps you identify which workloads might break after the transition. Starting from <strong>OpenShift 4.19+</strong>, the migration to cgroups v2 will be <strong>mandatory</strong> if not already performed, so it is important to assess your workloads sooner rather than later.</p>

<p>For a deep dive into how cgroups v2 impacts these runtimes, I recommend reading the Red Hat Developers article: <a href="https://developers.redhat.com/articles/2025/11/27/how-does-cgroups-v2-impact-java-net-and-nodejs-openshift-4">How does cgroups v2 impact Java, .NET, and Node.js on OpenShift 4?</a>.</p>

<h2 id="why-cgroups-v2-compatibility-matters">Why cgroups v2 compatibility matters</h2>

<p>Linux cgroups (control groups) are used by container runtimes to enforce resource limits (CPU, memory, etc.). Cgroups v2 is the successor to cgroups v1 and brings a unified hierarchy, improved resource management, and better support for rootless containers.</p>

<p>However, older versions of popular runtimes (Java, Node.js, .NET) read cgroup information from the filesystem to determine available resources. When a cluster switches from cgroups v1 to v2, the filesystem layout changes, and older runtimes may fail to detect resource limits correctly, leading to:</p>

<ul>
  <li><strong>Java</strong>: The JVM may see the host’s total memory instead of the container’s memory limit, potentially causing OOM kills</li>
  <li><strong>Node.js</strong>: Incorrect memory and CPU detection, leading to performance issues</li>
  <li><strong>.NET</strong>: Similar resource detection failures</li>
</ul>

<p>The minimum runtime versions that properly support cgroups v2 are:</p>

<table>
  <thead>
    <tr>
      <th>Runtime</th>
      <th>Minimum cgroups v2 compatible version</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>OpenJDK / HotSpot</td>
      <td>8u372, 11.0.16, 15+</td>
    </tr>
    <tr>
      <td>IBM Semeru</td>
      <td>8u345-b01, 11.0.16.0, 17.0.4.0, 18.0.2.0+</td>
    </tr>
    <tr>
      <td>IBM Java</td>
      <td>8.0.7.15+</td>
    </tr>
    <tr>
      <td>Node.js</td>
      <td>20.3.0+</td>
    </tr>
    <tr>
      <td>.NET</td>
      <td>5.0+</td>
    </tr>
  </tbody>
</table>

<p>For more details on the compatibility matrix, refer to the <a href="https://developers.redhat.com/articles/2025/11/27/how-does-cgroups-v2-impact-java-net-and-nodejs-openshift-4">Red Hat Developers article</a>.</p>

<h2 id="what-does-image-cgroupsv2-inspector-do">What does image-cgroupsv2-inspector do?</h2>

<p>The tool connects to your OpenShift cluster, collects all container images from running workloads, and then optionally analyzes each image by:</p>

<ol>
  <li><strong>Collecting images</strong>: Scans Deployments, DeploymentConfigs, StatefulSets, DaemonSets, CronJobs, ReplicaSets, Jobs, and standalone Pods to find all container images in use</li>
  <li><strong>Smart deduplication</strong>: Only reports top-level controllers (e.g., a Deployment but not its child ReplicaSets or Pods), avoiding duplicate entries</li>
  <li><strong>Image analysis</strong>: For each unique image, pulls it locally, extracts the filesystem, and searches for Java, Node.js, and .NET binaries</li>
  <li><strong>Version detection</strong>: Executes each binary to determine its exact version</li>
  <li><strong>Compatibility check</strong>: Compares detected versions against the known cgroups v2 minimum compatibility versions</li>
  <li><strong>CSV report</strong>: Generates a detailed CSV report with all findings</li>
</ol>

<h2 id="requirements">Requirements</h2>

<p>Before starting, you’ll need:</p>

<ul>
  <li>Access to an <strong>OpenShift 4.x</strong> cluster with a valid token</li>
  <li><strong>podman</strong> installed on the machine where you run the tool</li>
  <li><strong>Python 3.12+</strong></li>
  <li>At least <strong>20GB of free disk space</strong> for image extraction</li>
  <li>The <strong>acl</strong> package installed (for extended ACL support on the rootfs directory)</li>
  <li>Network access to all container registries used by your cluster</li>
</ul>

<h2 id="installation">Installation</h2>

<p>Clone the repository and set up a Python virtual environment:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone https://github.com/amedeos/image-cgroupsv2-inspector.git
<span class="nv">$ </span><span class="nb">cd </span>image-cgroupsv2-inspector
<span class="nv">$ </span>python3 <span class="nt">-m</span> venv venv
<span class="nv">$ </span><span class="nb">source </span>venv/bin/activate
<span class="nv">$ </span>pip <span class="nb">install</span> <span class="nt">-r</span> requirements.txt
</code></pre></div></div>

<h2 id="usage">Usage</h2>

<h3 id="connecting-to-the-cluster">Connecting to the cluster</h3>

<p>The first step is to connect to your OpenShift cluster. You can provide the API URL and token directly:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./image-cgroupsv2-inspector <span class="nt">--api-url</span> https://api.mycluster.example.com:6443 <span class="nt">--token</span> sha256~xxxxx
</code></pre></div></div>

<p>After the first successful connection, credentials are saved to an <code class="language-plaintext highlighter-rouge">.env</code> file so you don’t need to provide them again.</p>

<h3 id="collecting-images-without-analysis">Collecting images (without analysis)</h3>

<p>To simply list all container images in your cluster without analyzing them:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./image-cgroupsv2-inspector <span class="nt">--rootfs-path</span> /tmp/images
</code></pre></div></div>

<p>This produces output like:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>╔══════════════════════════════════════════════════════════════╗
║           image-cgroupsv2-inspector v1.0.0                   ║
║     OpenShift Container Image Inspector for cgroups v2       ║
╚══════════════════════════════════════════════════════════════╝

🔍 Running system checks...
✓ podman is installed: podman version 5.7.1
✓ podman is functional (OS: linux)

🔧 Setting up rootfs directory at: /tmp/images
✓ Write permission verified on /tmp/images
✓ Sufficient disk space: 32.0GB free (required: 20GB, total: 32.0GB)
✓ Filesystem supports extended ACLs
✓ Created directory: /tmp/images/rootfs
...

🔌 Connecting to OpenShift cluster...
✓ Connected to OpenShift cluster
  Kubernetes version: v1.30.14
  Cluster name: mycluster.example.com
✓ Credentials saved to .env
✓ Pull secret already exists at .pull-secret, skipping download

📋 Namespace exclusion patterns: openshift-*, kube-*

📦 Collecting container images from cluster...
  (Only top-level controllers are reported, child objects are skipped)
  (Excluding namespaces matching: openshift-*, kube-*)
  Collecting images from Deployments...
    Found 10 containers in Deployments
  Collecting images from DeploymentConfigs...
    Found 2 containers in DeploymentConfigs
  Collecting images from StatefulSets...
    Found 0 containers in StatefulSets
  Collecting images from DaemonSets...
    Found 0 containers in DaemonSets
  Collecting images from CronJobs...
    Found 0 containers in CronJobs
  Collecting images from standalone ReplicaSets...
    Found 0 containers in standalone ReplicaSets (skipped 10 managed/empty)
  Collecting images from standalone Jobs...
    Found 0 containers in standalone Jobs (skipped 0 CronJob-managed)
  Collecting images from standalone Pods...
    Found 0 containers in standalone Pods (skipped 307 managed/static pods)

✓ Total containers found: 12
  (Excluded 51 namespaces: openshift-apiserver, openshift-apiserver-operator, ...)

📊 Summary:
   Total containers: 12
   Unique images: 10
   Namespaces: 6
   Object types: {'Deployment': 10, 'DeploymentConfig': 2}

   Output file: output/mycluster.example.com-20260309-085433.csv

📋 Top 10 most used images:
      2 × registry.access.redhat.com/ubi8/openjdk-17:latest
      2 × registry.access.redhat.com/ubi8/openjdk-8:1.14
      1 × registry.redhat.io/ubi8/dotnet-30:latest
      1 × registry.redhat.io/dotnet/sdk:8.0.122
      1 × image-registry.openshift-image-registry.svc:5000/test-java-internalreg/openjdk-17:latest
      1 × image-registry.openshift-image-registry.svc:5000/test-java-internalreg/openjdk-8:1.14
      1 × docker.io/library/eclipse-temurin:8u302-b08-jdk-centos7
      1 × docker.io/library/eclipse-temurin:17
      1 × registry.access.redhat.com/ubi8/nodejs-20:latest
      1 × registry.access.redhat.com/ubi8/nodejs-18:latest
✓ Disconnected from OpenShift cluster

✓ Done!
</code></pre></div></div>

<p>By default, the tool excludes OpenShift internal namespaces (<code class="language-plaintext highlighter-rouge">openshift-*</code>, <code class="language-plaintext highlighter-rouge">kube-*</code>) since those are managed by the platform itself.</p>

<h3 id="full-analysis-with-cgroups-v2-compatibility-check">Full analysis with cgroups v2 compatibility check</h3>

<p>To analyze all images and determine cgroups v2 compatibility, add the <code class="language-plaintext highlighter-rouge">--analyze</code> flag:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./image-cgroupsv2-inspector <span class="nt">--rootfs-path</span> /tmp/images <span class="nt">--analyze</span>
</code></pre></div></div>

<p>The tool will pull each unique image, extract its filesystem, search for Java/Node.js/.NET binaries, run version checks, and report compatibility:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>🔬 Analyzing images for Java, NodeJS, and .NET binaries...
  (Each image will be pulled, analyzed, and cleaned up)
  (CSV will be saved after each image for resumability)
  Found 10 unique images to analyze

  [1/10] Analyzing: image-registry.openshift-image-registry.svc:5000/test-java-internalreg...
    Pulling image: image-registry.openshift-image-registry.svc:5000/test-java-internalreg/openjdk-17:latest...
    Exporting container filesystem...
    Extracting filesystem...
    Searching for Java binaries...
    Searching for Node.js binaries...
    Searching for .NET binaries...
      ✓ Java (OpenJDK): 17.0.18 at /usr/lib/jvm/jre-17-openjdk-17.0.18.0.8-1.el8.x86_64/bin/java
    💾 Progress saved: 1 rows (1/10 images)
...
  [5/10] Analyzing: registry.redhat.io/ubi8/dotnet-30:latest...
    Pulling image: registry.redhat.io/ubi8/dotnet-30:latest...
    Exporting container filesystem...
    Extracting filesystem...
    Searching for Java binaries...
    Searching for Node.js binaries...
    Searching for .NET binaries...
      ✗ Node.js: 10.19.0 at /usr/bin/node
      ✗ .NET: 3.0.3 at /usr/lib64/dotnet/dotnet
    💾 Progress saved: 6 rows (5/10 images)
...
</code></pre></div></div>

<p>Notice the symbols: <strong>checkmark</strong> means the runtime is compatible with cgroups v2, while <strong>cross</strong> means it is <strong>not</strong> compatible and needs to be upgraded.</p>

<p>At the end of the analysis, a summary is printed:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>📊 Summary:
   Total containers: 12
   Unique images: 10
   Namespaces: 6
   Object types: {'Deployment': 10, 'DeploymentConfig': 2}

   🔬 Analysis Results:
      Java found in: 8 containers
        ✓ cgroup v2 compatible: 4
        ✗ cgroup v2 incompatible: 4
      Node.js found in: 3 containers
        ✓ cgroup v2 compatible: 1
        ✗ cgroup v2 incompatible: 2
      .NET found in: 2 containers
        ✓ cgroup v2 compatible: 1
        ✗ cgroup v2 incompatible: 1

   Output file: output/mycluster.example.com-20260309-085455.csv
</code></pre></div></div>

<h3 id="analyzing-a-single-namespace">Analyzing a single namespace</h3>

<p>If you want to focus on a specific namespace, use the <code class="language-plaintext highlighter-rouge">-n</code> flag:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./image-cgroupsv2-inspector <span class="nt">--rootfs-path</span> /tmp/images <span class="nt">--analyze</span> <span class="nt">-n</span> test-java
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>📋 Inspecting single namespace: test-java

📦 Collecting container images from namespace: test-java
  (Only top-level controllers are reported, child objects are skipped)
  Collecting images from Deployments...
    Found 2 containers in Deployments
...

🔬 Analyzing images for Java, NodeJS, and .NET binaries...
  Found 2 unique images to analyze

  [1/2] Analyzing: registry.access.redhat.com/ubi8/openjdk-8:1.14...
    Pulling image: registry.access.redhat.com/ubi8/openjdk-8:1.14...
    Exporting container filesystem...
    Extracting filesystem...
    Searching for Java binaries...
    Searching for Node.js binaries...
    Searching for .NET binaries...
      ✗ Java (OpenJDK): 1.8.0_362 at /usr/lib/jvm/jre-1.8.0-openjdk-1.8.0.362.b09-2.el8_7.x86_64/bin/java
      ✗ Java (OpenJDK): 1.8.0_362 at /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.362.b09-2.el8_7.x86_64/bin/java
    💾 Progress saved: 1 rows (1/2 images)

  [2/2] Analyzing: registry.access.redhat.com/ubi8/openjdk-17:latest...
    Pulling image: registry.access.redhat.com/ubi8/openjdk-17:latest...
    Exporting container filesystem...
    Extracting filesystem...
    Searching for Java binaries...
    Searching for Node.js binaries...
    Searching for .NET binaries...
      ✓ Java (OpenJDK): 17.0.18 at /usr/lib/jvm/jre-17-openjdk-17.0.18.0.8-1.el8.x86_64/bin/java
    💾 Progress saved: 2 rows (2/2 images)

✓ Analyzed 2 unique images

📊 Summary:
   Total containers: 2
   Unique images: 2
   Namespaces: 1
   Object types: {'Deployment': 2}

   🔬 Analysis Results:
      Java found in: 2 containers
        ✓ cgroup v2 compatible: 1
        ✗ cgroup v2 incompatible: 1
      Node.js found in: 0 containers
      .NET found in: 0 containers
</code></pre></div></div>

<p>In this example, one container uses <strong>OpenJDK 17.0.18</strong> (compatible) while the other uses <strong>OpenJDK 1.8.0_362</strong> (incompatible, since the minimum required for Java 8 is <strong>8u372</strong>).</p>

<h2 id="understanding-the-csv-output">Understanding the CSV output</h2>

<p>The tool generates a CSV file in the <code class="language-plaintext highlighter-rouge">output/</code> directory, named after the cluster and timestamp. Here’s what the columns mean:</p>

<table>
  <thead>
    <tr>
      <th>Column</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">container_name</code></td>
      <td>Name of the container</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">namespace</code></td>
      <td>Kubernetes namespace</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">object_type</code></td>
      <td>Resource type (Deployment, StatefulSet, etc.)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">object_name</code></td>
      <td>Name of the parent resource</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">image_name</code></td>
      <td>Full image name with tag</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">java_binary</code></td>
      <td>Path to Java binary found (“None” if not found)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">java_version</code></td>
      <td>Detected Java version</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">java_cgroup_v2_compatible</code></td>
      <td>“Yes”, “No”, or “N/A”</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">node_binary</code></td>
      <td>Path to Node.js binary found</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">node_version</code></td>
      <td>Detected Node.js version</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">node_cgroup_v2_compatible</code></td>
      <td>“Yes”, “No”, or “N/A”</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dotnet_binary</code></td>
      <td>Path to .NET binary found</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dotnet_version</code></td>
      <td>Detected .NET version</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dotnet_cgroup_v2_compatible</code></td>
      <td>“Yes”, “No”, or “N/A”</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">analysis_error</code></td>
      <td>Error message if analysis failed</td>
    </tr>
  </tbody>
</table>

<p>The CSV can be easily imported into a spreadsheet or processed with standard tools to generate reports for your team.</p>

<h2 id="how-the-analysis-works-under-the-hood">How the analysis works under the hood</h2>

<p>For each unique container image, the tool performs the following steps:</p>

<ol>
  <li><strong>Pull the image</strong> using <code class="language-plaintext highlighter-rouge">podman pull</code> with the cluster’s pull-secret for authentication</li>
  <li><strong>Create a temporary container</strong> and export its filesystem as a tar archive</li>
  <li><strong>Extract the tar</strong> to a local rootfs directory</li>
  <li><strong>Search for binaries</strong> by walking the extracted filesystem looking for <code class="language-plaintext highlighter-rouge">java</code>, <code class="language-plaintext highlighter-rouge">node</code>, and <code class="language-plaintext highlighter-rouge">dotnet</code> executables (skipping symlinks that point to already-found binaries)</li>
  <li><strong>Execute version checks</strong> by running <code class="language-plaintext highlighter-rouge">podman run</code> with the original image, overriding the entrypoint to call the binary with its version flag (<code class="language-plaintext highlighter-rouge">java -version</code>, <code class="language-plaintext highlighter-rouge">node --version</code>, <code class="language-plaintext highlighter-rouge">dotnet --list-runtimes</code>)</li>
  <li><strong>Compare versions</strong> against the known minimum compatible versions</li>
  <li><strong>Clean up</strong> by removing the extracted files, tar archive, and the pulled image</li>
</ol>

<p>The tool handles several edge cases:</p>

<ul>
  <li><strong>OpenShift internal registry images</strong>: Automatically detects the internal registry route and rewrites URLs for external access</li>
  <li><strong>Short-name image resolution</strong>: Resolves short image names (e.g., <code class="language-plaintext highlighter-rouge">eclipse-temurin:17</code>) to their fully qualified domain name by querying pod status</li>
  <li><strong>Resumability</strong>: The CSV is saved after each image is analyzed, so if the process is interrupted, already-analyzed images are preserved</li>
</ul>

<h2 id="additional-options">Additional options</h2>

<h3 id="custom-namespace-exclusions">Custom namespace exclusions</h3>

<p>By default, <code class="language-plaintext highlighter-rouge">openshift-*</code> and <code class="language-plaintext highlighter-rouge">kube-*</code> namespaces are excluded. You can customize this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./image-cgroupsv2-inspector <span class="nt">--rootfs-path</span> /tmp/images <span class="nt">--analyze</span> <span class="se">\</span>
    <span class="nt">--exclude-namespaces</span> <span class="s2">"openshift-*,kube-*,staging-*"</span>
</code></pre></div></div>

<h3 id="verbose-mode">Verbose mode</h3>

<p>For detailed debugging output, add the <code class="language-plaintext highlighter-rouge">-v</code> flag:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./image-cgroupsv2-inspector <span class="nt">--rootfs-path</span> /tmp/images <span class="nt">--analyze</span> <span class="nt">-v</span>
</code></pre></div></div>

<h3 id="log-to-file">Log to file</h3>

<p>To save all output to a log file for later review:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./image-cgroupsv2-inspector <span class="nt">--rootfs-path</span> /tmp/images <span class="nt">--analyze</span> <span class="se">\</span>
    <span class="nt">--log-to-file</span> <span class="nt">--log-file</span> my-analysis.log
</code></pre></div></div>

<h3 id="skip-disk-check">Skip disk check</h3>

<p>If you know your disk has enough space and want to skip the 20GB check:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./image-cgroupsv2-inspector <span class="nt">--rootfs-path</span> /tmp/images <span class="nt">--analyze</span> <span class="nt">--skip-disk-check</span>
</code></pre></div></div>

<h3 id="custom-internal-registry-route">Custom internal registry route</h3>

<p>If your OpenShift cluster exposes the internal registry with a custom route:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./image-cgroupsv2-inspector <span class="nt">--rootfs-path</span> /tmp/images <span class="nt">--analyze</span> <span class="se">\</span>
    <span class="nt">--internal-registry-route</span> my-registry.apps.mycluster.example.com
</code></pre></div></div>

<h2 id="conclusion">Conclusion</h2>

<p>Before upgrading your OpenShift cluster to a version that defaults to cgroups v2, it is critical to assess your workloads for compatibility. The <strong>image-cgroupsv2-inspector</strong> tool automates this process by scanning all your container images, detecting Java, Node.js, and .NET runtimes, and checking whether they meet the minimum version requirements for cgroups v2 support.</p>

<p>The source code is available on GitHub: <a href="https://github.com/amedeos/image-cgroupsv2-inspector">image-cgroupsv2-inspector</a>.</p>

<p>For a comprehensive explanation of how cgroups v2 impacts these runtimes, check out the Red Hat Developers article: <a href="https://developers.redhat.com/articles/2025/11/27/how-does-cgroups-v2-impact-java-net-and-nodejs-openshift-4">How does cgroups v2 impact Java, .NET, and Node.js on OpenShift 4?</a>.</p>]]></content><author><name></name></author><category term="OpenShift" /><category term="OpenShift" /><category term="cgroupsv2" /><category term="java" /><category term="nodejs" /><category term="dotnet" /><summary type="html"><![CDATA[In my daily work I regularly help customers plan their OpenShift upgrades, and one of the most common concerns when moving to cgroups v2 is understanding which workloads are affected. I needed a quick and reliable way to scan an entire cluster and identify incompatible Java, Node.js, and .NET runtimes, so I built image-cgroupsv2-inspector and released it as open source, so that anyone can use it, modify it, and contribute to it.]]></summary></entry><entry><title type="html">Change OpenShift Data Foundation OSDs disk flavor (dimension)</title><link href="/openshift/2023/02/26/Change-ODF-osd-disk-flavor.html" rel="alternate" type="text/html" title="Change OpenShift Data Foundation OSDs disk flavor (dimension)" /><published>2023-02-26T09:00:00+00:00</published><updated>2023-02-26T09:00:00+00:00</updated><id>/openshift/2023/02/26/Change-ODF-osd-disk-flavor</id><content type="html" xml:base="/openshift/2023/02/26/Change-ODF-osd-disk-flavor.html"><![CDATA[<p>In this article, I’ll show you how to migrate your <strong>OpenShift Data Foundation OSDs</strong> (disks) from one flavor to another; in my case, I’ll migrate OSDs and data from 0.5TiB disks to 2TiB disks; this will be a “rolling” migration with no service or data disruption.</p>

<p><strong>Warning:</strong> If you are a Red Hat customer, open a support case before going forward, otherwise do the following steps at your own risk!</p>

<h2 id="requirements">Requirements</h2>
<p>Before starting, you’ll need:</p>

<ul>
  <li>installed and working OpenShift Data Foundation;</li>
  <li>this article is based on ODF configured with the <strong>replica</strong> parameter set to [0], which is usually the default on hyperscalers; otherwise, you’ll need to adapt if you want to do this migration, for example, on bare metal (perhaps you’re using the LocalStorage operator on bare metal).</li>
  <li>in this guide, I’ll move data and disks from three OSD disks to three other OSD disks; if you have more than three OSD, you must redo this procedure from beginning to end or check if the destination three disks can store data from more than three source disks.</li>
</ul>

<p>[0] In this guide, I’ll assume your OpenShift Data Foundation is installed on a hyperscale cloud provider, such as Azure or AWS, with three availability zones, and that you have replica set to three:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc get storagecluster <span class="nt">-n</span> openshift-storage ocs-storagecluster <span class="nt">-ojson</span> | jq .spec.storageDeviceSets
<span class="o">[</span>
  <span class="o">{</span>
    <span class="s2">"count"</span>: 1,
...
    <span class="s2">"replica"</span>: 3,
...
  <span class="o">}</span>
<span class="o">]</span>
</code></pre></div></div>

<h2 id="run-must-gather">Run must-gather</h2>
<p>Before applying any change, run an OpenShift must-gather:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc adm must-gather
</code></pre></div></div>

<p>then create a specific ODF must-gather; in this example, I use ODF in version 4.10:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">mkdir</span> ~/odf-must-gather
<span class="nv">$ </span>oc adm must-gather <span class="nt">--image</span><span class="o">=</span>registry.redhat.io/odf4/ocs-must-gather-rhel8:v4.10 <span class="nt">--dest-dir</span><span class="o">=</span>~/odf-must-gather
</code></pre></div></div>

<h2 id="check-cluster-health">Check cluster health</h2>
<p>Check if your cluster is healthy:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ NAMESPACE</span><span class="o">=</span>openshift-storage<span class="p">;</span><span class="nv">ROOK_POD</span><span class="o">=</span><span class="si">$(</span>oc <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> get pod <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>rook-ceph-operator <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[0].metadata.name}'</span><span class="si">)</span><span class="p">;</span>oc <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">ROOK_POD</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--</span> ceph status <span class="nt">--cluster</span><span class="o">=</span><span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--conf</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>.config <span class="nt">--keyring</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/client.admin.keyring | <span class="nb">grep </span>HEALTH
    health: HEALTH_OK
</code></pre></div></div>

<p><strong>WARNING:</strong> if your cluster is not in <strong>HEALTH_OK</strong>, stop any activities and check the ODF state!</p>

<h2 id="add-capacity">Add Capacity</h2>
<p>Add new capacity to your cluster using the new OSD flavor; in my case, the original storageDeviceSets is using 0.5TiB disks:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc get storagecluster ocs-storagecluster <span class="nt">-n</span> openshift-storage <span class="nt">-oyaml</span>
...
  storageDeviceSets:
  - config: <span class="o">{}</span>
    count: 1
    dataPVCTemplate:
      metadata: <span class="o">{}</span>
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 500Gi
        storageClassName: gp3-csi
        volumeMode: Block
      status: <span class="o">{}</span>
    name: ocs-deviceset
    placement: <span class="o">{}</span>
    preparePlacement: <span class="o">{}</span>
    replica: 3
    resources:
      limits:
        cpu: <span class="s2">"2"</span>
        memory: 5Gi
      requests:
        cpu: <span class="s2">"2"</span>
        memory: 5Gi
</code></pre></div></div>

<p>switch to openshift-storage project and backup storagecluster:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc project openshift-storage

<span class="nv">$ </span>oc get storagecluster ocs-storagecluster <span class="nt">-oyaml</span> | <span class="nb">tee </span>backup-storagecluster-ocs-storagecluster.yaml
</code></pre></div></div>

<p>add new OSDs, with desired flavor, in my case I’m adding a new storageDeviceSets with 2TiB disks:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc edit storagecluster ocs-storagecluster
...
  storageDeviceSets:
  - config: <span class="o">{}</span>
    count: 1
    dataPVCTemplate:
      metadata: <span class="o">{}</span>
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 500Gi
        storageClassName: gp3-csi
        volumeMode: Block
      status: <span class="o">{}</span>
    name: ocs-deviceset
    placement: <span class="o">{}</span>
    preparePlacement: <span class="o">{}</span>
    replica: 3
    resources:
      limits:
        cpu: <span class="s2">"2"</span>
        memory: 5Gi
      requests:
        cpu: <span class="s2">"2"</span>
        memory: 5Gi
  - config: <span class="o">{}</span>
    count: 1
    dataPVCTemplate:
      metadata: <span class="o">{}</span>
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 2000Gi
        storageClassName: gp3-csi
        volumeMode: Block
      status: <span class="o">{}</span>
    name: ocs-deviceset-2t
    placement: <span class="o">{}</span>
    preparePlacement: <span class="o">{}</span>
    replica: 3
    resources:
      limits:
        cpu: <span class="s2">"2"</span>
        memory: 5Gi
      requests:
        cpu: <span class="s2">"2"</span>
        memory: 5Gi
  version: 4.10.0
</code></pre></div></div>

<p>wait until ODF will rebalance all data, which means cluster will be in <strong>HEALTH_OK</strong> status and all placement groups (<strong>pgs</strong>) must be in <strong>active+clean</strong> state. To monitor rebalance, you can use a while true infinite loop:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do </span><span class="nv">NAMESPACE</span><span class="o">=</span>openshift-storage<span class="p">;</span><span class="nv">ROOK_POD</span><span class="o">=</span><span class="si">$(</span>oc <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> get pod <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>rook-ceph-operator <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[0].metadata.name}'</span><span class="si">)</span><span class="p">;</span>oc <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">ROOK_POD</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--</span> ceph status <span class="nt">--cluster</span><span class="o">=</span><span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--conf</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>.config <span class="nt">--keyring</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/client.admin.keyring | egrep <span class="s1">'HEALTH_OK|HEALTH_WARN|[0-9]+\s+remapped|[0-9]+\/[0-9]+[ a-z]+misplaced[ ().%a-z0-9]+|'</span> <span class="p">;</span> <span class="nb">sleep </span>10 <span class="p">;</span> <span class="k">done
  </span>cluster:                  
    <span class="nb">id</span>:     .....
    health: HEALTH_OK                                              
                                                  
  services:                                         
    mon: 3 daemons, quorum a,b,c <span class="o">(</span>age 2h<span class="o">)</span>     
    mgr: a<span class="o">(</span>active, since 2w<span class="o">)</span> 
    mds: 1/1 daemons up, 1 hot standby
    osd: 6 osds: 6 up <span class="o">(</span>since 34s<span class="o">)</span>, 6 <span class="k">in</span> <span class="o">(</span>since 51s<span class="o">)</span><span class="p">;</span> 54 remapped pgs
                                                    
  data:                                                        
    volumes: 1/1 healthy            
    pools:   4 pools, 97 pgs                
    objects: 17.77k objects, 54 GiB
    usage:   157 GiB used, 7.2 TiB / 7.3 TiB avail
    pgs:     45509/53298 objects misplaced <span class="o">(</span>85.386%<span class="o">)</span>        
             54 active+remapped+backfill_wait
             37 active+clean
             5  active+remapped
             1  active+remapped+backfilling

  io:
    client:   1023 B/s rd, 217 KiB/s wr, 1 op/s rd, 6 op/s wr
    recovery: 86 MiB/s, 1 keys/s, 30 objects/s
</code></pre></div></div>

<p>in the above example, you can see that Ceph is rebalancing and remapping PGs. Wait until all PGs are in <strong>active+clean</strong> state:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ NAMESPACE</span><span class="o">=</span>openshift-storage<span class="p">;</span><span class="nv">ROOK_POD</span><span class="o">=</span><span class="si">$(</span>oc <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> get pod <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>rook-ceph-operator <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[0].metadata.name}'</span><span class="si">)</span><span class="p">;</span>oc <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">ROOK_POD</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--</span> ceph status <span class="nt">--cluster</span><span class="o">=</span><span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--conf</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>.config <span class="nt">--keyring</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/client.admin.keyring
  cluster:
    <span class="nb">id</span>:     .....
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum a,b,c <span class="o">(</span>age 2h<span class="o">)</span>
    mgr: a<span class="o">(</span>active, since 2w<span class="o">)</span>
    mds: 1/1 daemons up, 1 hot standby
    osd: 6 osds: 6 up <span class="o">(</span>since 17m<span class="o">)</span>, 6 <span class="k">in</span> <span class="o">(</span>since 18m<span class="o">)</span>
 
  data:
    volumes: 1/1 healthy
    pools:   4 pools, 193 pgs
    objects: 17.02k objects, 51 GiB
    usage:   146 GiB used, 7.2 TiB / 7.3 TiB avail
    pgs:     193 active+clean
 
  io:
    client:   853 B/s rd, 76 KiB/s wr, 1 op/s rd, 7 op/s wr
</code></pre></div></div>

<p><strong>WARNING:</strong> wait until your cluster returns all PGs in <strong>active+clean</strong> state!</p>

<p>check also CephFS status:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ NAMESPACE</span><span class="o">=</span>openshift-storage<span class="p">;</span><span class="nv">ROOK_POD</span><span class="o">=</span><span class="si">$(</span>oc <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> get pod <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>rook-ceph-operator <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[0].metadata.name}'</span><span class="si">)</span><span class="p">;</span>oc <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">ROOK_POD</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--</span> ceph fs status <span class="nt">--cluster</span><span class="o">=</span><span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--conf</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>.config <span class="nt">--keyring</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/client.admin.keyring
ocs-storagecluster-cephfilesystem - 12 clients
<span class="o">=================================</span>
RANK      STATE                       MDS                     ACTIVITY     DNS    INOS   DIRS   CAPS  
 0        active      ocs-storagecluster-cephfilesystem-b  Reqs:   37 /s  34.8k  27.2k  8369   27.1k  
0-s   standby-replay  ocs-storagecluster-cephfilesystem-a  Evts:   47 /s  82.3k  26.8k  8298      0   
</code></pre></div></div>

<p><strong>WARNING</strong>: one of the two MDS must be in active state!</p>

<h2 id="identify-old-osds--disks-to-remove">Identify old OSDs / disks to remove</h2>
<p>Take a note of your 3 OSD id to remove, they are based on your old flavor (weight 0.48830), to see ODF OSD topology run:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ NAMESPACE</span><span class="o">=</span>openshift-storage<span class="p">;</span><span class="nv">ROOK_POD</span><span class="o">=</span><span class="si">$(</span>oc <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> get pod <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>rook-ceph-operator <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[0].metadata.name}'</span><span class="si">)</span><span class="p">;</span>oc <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">ROOK_POD</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--</span> ceph osd tree <span class="nt">--cluster</span><span class="o">=</span><span class="k">${</span><span class="nv">NA</span><span class="p">
MESPACE</span><span class="k">}</span> <span class="nt">--conf</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>.config <span class="nt">--keyring</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/client.admin.keyring
ID   CLASS  WEIGHT   TYPE NAME                                       STATUS  REWEIGHT  PRI-AFF
 <span class="nt">-1</span>         7.32417  root default
 <span class="nt">-5</span>         7.32417      region eu-central-1
<span class="nt">-14</span>         2.44139          zone eu-central-1a
<span class="nt">-13</span>         2.44139              host ip-XX-XX-XX-4-rete
  2    ssd  0.48830                  osd.2                               up   1.00000  1.00000
  5    ssd  1.95309                  osd.5                               up   1.00000  1.00000
<span class="nt">-10</span>         2.44139          zone eu-central-1b
 <span class="nt">-9</span>         2.44139              host ip-XX-XX-XX-46-rete
  1    ssd  0.48830                  osd.1                               up   1.00000  1.00000
  4    ssd  1.95309                  osd.4                               up   1.00000  1.00000
 <span class="nt">-4</span>         2.44139          zone eu-central-1c
 <span class="nt">-3</span>         2.44139              host ip-XX-XX-XX-80-rete
  0    ssd  0.48830                  osd.0                               up   1.00000  1.00000
  3    ssd  1.95309                  osd.3                               up   1.00000  1.00000
</code></pre></div></div>

<p>In my case, the old OSD are osd.0, osd.1 and osd.2. Those OSDs need to be removed / deleted one by one, waiting for <strong>HEALTH_OK</strong> after every removal / deletion.</p>

<h2 id="remove-the-osd-from-the-old-storage-flavor">Remove the OSD from the old Storage flavor</h2>
<h3 id="switch-to-the-openshift-storage-project">Switch to the openshift-storage project</h3>
<p>First switch to the openshift-storage project:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc project openshift-storage
</code></pre></div></div>

<h3 id="copy-the-ceph-config-and-keyring-files">Copy the Ceph config and keyring files</h3>
<p>Copy your Ceph config file and keyring file from rook container pod to your Linux box, and then those files will be transferred to one mon container in order to run Ceph commands after scaling down rook operator.</p>

<p>Copy files from rook container to your Linux box:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ ROOK</span><span class="o">=</span><span class="si">$(</span>oc get pod | <span class="nb">grep </span>rook-ceph-operator | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>
<span class="nv">$ </span><span class="nb">echo</span> <span class="k">${</span><span class="nv">ROOK</span><span class="k">}</span>
rook-ceph-operator-5767bbc7b9-w8swd

<span class="nv">$ </span>oc rsync <span class="k">${</span><span class="nv">ROOK</span><span class="k">}</span>:/var/lib/rook/openshift-storage/openshift-storage.config <span class="nb">.</span>
WARNING: cannot use rsync: rsync not available <span class="k">in </span>container
openshift-storage.config
<span class="nv">$ </span>oc rsync <span class="k">${</span><span class="nv">ROOK</span><span class="k">}</span>:/var/lib/rook/openshift-storage/client.admin.keyring <span class="nb">.</span>
WARNING: cannot use rsync: rsync not available <span class="k">in </span>container
client.admin.keyring
</code></pre></div></div>

<p>Copy the openshift-storage.config and openshift-storage.config files from your Linux box to one mon container:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ MONA</span><span class="o">=</span><span class="si">$(</span>oc get pod | <span class="nb">grep </span>rook-ceph-mon | egrep <span class="s1">'2\/2\s+Running'</span> | <span class="nb">head</span> <span class="nt">-n1</span> | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>
<span class="nv">$ </span><span class="nb">echo</span> <span class="k">${</span><span class="nv">MONA</span><span class="k">}</span>
rook-ceph-mon-a-769fc864f-btmmr

<span class="nv">$ </span>oc <span class="nb">cp </span>openshift-storage.config <span class="k">${</span><span class="nv">MONA</span><span class="k">}</span>:/tmp/openshift-storage.config
Defaulted container <span class="s2">"mon"</span> out of: mon, log-collector, chown-container-data-dir <span class="o">(</span>init<span class="o">)</span>, init-mon-fs <span class="o">(</span>init<span class="o">)</span>
<span class="nv">$ </span>oc <span class="nb">cp </span>client.admin.keyring <span class="k">${</span><span class="nv">MONA</span><span class="k">}</span>:/tmp/client.admin.keyring
Defaulted container <span class="s2">"mon"</span> out of: mon, log-collector, chown-container-data-dir <span class="o">(</span>init<span class="o">)</span>, init-mon-fs <span class="o">(</span>init<span class="o">)</span>
</code></pre></div></div>

<p><strong>NOTE</strong>: MONA, in one of Italian regional language means stupid people :smile:</p>

<p>Check the Ceph command on MONA container:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc rsh <span class="k">${</span><span class="nv">MONA</span><span class="k">}</span>
Defaulted container <span class="s2">"mon"</span> out of: mon, log-collector, chown-container-data-dir <span class="o">(</span>init<span class="o">)</span>, init-mon-fs <span class="o">(</span>init<span class="o">)</span>
sh-4.4# ceph health <span class="nt">--cluster</span><span class="o">=</span>openshift-storage <span class="nt">--conf</span><span class="o">=</span>/tmp/openshift-storage.config <span class="nt">--keyring</span><span class="o">=</span>/tmp/client.admin.keyring
2023-XX <span class="nt">-1</span> auth: unable to find a keyring on /var/lib/rook/openshift-storage/client.admin.keyring: <span class="o">(</span>2<span class="o">)</span> No such file or directory
2023-XX <span class="nt">-1</span> AuthRegistry<span class="o">(</span>0x7fbbb805bb68<span class="o">)</span> no keyring found at /var/lib/rook/openshift-storage/client.admin.keyring, disabling cephx
HEALTH_OK
sh-4.4# <span class="nb">exit</span>
</code></pre></div></div>

<h3 id="scale-down-openshift-data-foundation-operators">Scale down OpenShift Data Foundation operators</h3>
<p>We can now scale to zero rook and ocs operators:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc scale deploy ocs-operator <span class="nt">--replicas</span><span class="o">=</span>0
deployment.apps/ocs-operator scaled
<span class="nv">$ </span>oc scale deploy rook-ceph-operator <span class="nt">--replicas</span><span class="o">=</span>0
deployment.apps/rook-ceph-operator scaled
</code></pre></div></div>

<h3 id="remove-one-osd">Remove one OSD</h3>
<p>Now you can remove one OSD; in my case, I’ll remove osd.0 (zero), but in your case, it could be a different ID.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ failed_osd_id</span><span class="o">=</span>0
<span class="nv">$ </span><span class="nb">export </span><span class="nv">PS1</span><span class="o">=</span><span class="s2">"[</span><span class="se">\u</span><span class="s2">@</span><span class="se">\h</span><span class="s2"> </span><span class="se">\W</span><span class="s2">]</span><span class="se">\ </span><span class="s2">OSD=</span><span class="nv">$failed_osd_id</span><span class="s2"> </span><span class="nv">$ </span><span class="s2">"</span>

<span class="nv">$ </span>oc scale deploy rook-ceph-osd-<span class="k">${</span><span class="nv">failed_osd_id</span><span class="k">}</span> <span class="nt">--replicas</span><span class="o">=</span>0
deployment.apps/rook-ceph-osd-0 scaled

<span class="nv">$ </span>oc process <span class="nt">-n</span> openshift-storage ocs-osd-removal <span class="nt">-p</span> <span class="nv">FAILED_OSD_IDS</span><span class="o">=</span><span class="k">${</span><span class="nv">failed_osd_id</span><span class="k">}</span> <span class="nv">FORCE_OSD_REMOVAL</span><span class="o">=</span><span class="nb">true</span> |oc create <span class="nt">-n</span> openshift-storage <span class="nt">-f</span> -
job.batch/ocs-osd-removal-job created

<span class="nv">$ JOBREMOVAL</span><span class="o">=</span><span class="si">$(</span>oc get pod | <span class="nb">grep </span>ocs-osd-removal-job- | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>

<span class="nv">$ </span>oc logs <span class="k">${</span><span class="nv">JOBREMOVAL</span><span class="k">}</span> | egrep <span class="s2">"cephosd: completed removal of OSD </span><span class="k">${</span><span class="nv">failed_osd_id</span><span class="k">}</span><span class="s2">"</span>
2023-XX I | cephosd: completed removal of OSD 0
</code></pre></div></div>

<p><strong>NOTE</strong>: on the last command you must see <strong>cephosd: completed removal of OSD X</strong>, where X is your osd id (in my case zero).</p>

<p>Check the Ceph health status, where you can see a degraded state due to one osd removal:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc rsh <span class="k">${</span><span class="nv">MONA</span><span class="k">}</span>
Defaulted container <span class="s2">"mon"</span> out of: mon, log-collector, chown-container-data-dir <span class="o">(</span>init<span class="o">)</span>, init-mon-fs <span class="o">(</span>init<span class="o">)</span>
sh-4.4# 
sh-4.4# ceph status <span class="nt">--cluster</span><span class="o">=</span>openshift-storage <span class="nt">--conf</span><span class="o">=</span>/tmp/openshift-storage.config <span class="nt">--keyring</span><span class="o">=</span>/tmp/client.admin.keyring
2023-XX <span class="nt">-1</span> auth: unable to find a keyring on /var/lib/rook/openshift-storage/client.admin.keyring: <span class="o">(</span>2<span class="o">)</span> No such file or directory
2023-XX <span class="nt">-1</span> AuthRegistry<span class="o">(</span>0x7f207005bb68<span class="o">)</span> no keyring found at /var/lib/rook/openshift-storage/client.admin.keyring, disabling cephx
  cluster:
    <span class="nb">id</span>:     .....
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum a,b,c <span class="o">(</span>age 2h<span class="o">)</span>
    mgr: a<span class="o">(</span>active, since 2w<span class="o">)</span>
    mds: 1/1 daemons up, 1 hot standby
    osd: 5 osds: 5 up <span class="o">(</span>since 19m<span class="o">)</span>, 5 <span class="k">in</span> <span class="o">(</span>since 9m<span class="o">)</span>
 
  data:
    volumes: 1/1 healthy
    pools:   4 pools, 193 pgs
    objects: 17.10k objects, 52 GiB
    usage:   146 GiB used, 6.7 TiB / 6.8 TiB avail
    pgs:     193 active+clean
 
  io:
    client:   1.2 KiB/s rd, 460 KiB/s wr, 2 op/s rd, 7 op/s wr
 
sh-4.4#
</code></pre></div></div>

<p>wait until Ceph returns HEALTH_OK and all PGs are in <strong>active+clean</strong> state:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sh-4.4# <span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do </span>ceph status <span class="nt">--cluster</span><span class="o">=</span>openshift-storage <span class="nt">--conf</span><span class="o">=</span>/tmp/openshift-storage.config <span class="nt">--keyring</span><span class="o">=</span>/tmp/client.admin.keyring | egrep <span class="nt">--color</span><span class="o">=</span>always <span class="s1">'[0-9]+\/[0-9]+.*(degraded|misplaced)|'</span> <span class="p">;</span> <span class="nb">sleep </span>10 <span class="p">;</span> <span class="k">done</span>
</code></pre></div></div>

<p><strong>WARNING</strong>: before proceeding, you must wait for Ceph <strong>HEALTH_OK</strong> and all PGs in <strong>active+clean</strong> state!</p>

<p>Delete removal job:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc delete job ocs-osd-removal-job
job.batch <span class="s2">"ocs-osd-removal-job"</span> deleted
</code></pre></div></div>

<p>Repeat these steps for each OSD you need to remove (in my case for osd.1 and osd.2)</p>

<h2 id="remove-your-old-storagedevicesets-pointing-to-old-osd-disks-flavor">Remove your old storageDeviceSets pointing to old OSD disks flavor</h2>
<p>After removing all OSD from your old storageDeviceSets (in my case, with disk flavor set to 0.5TiB), you can remove it from your <strong>storagecluster</strong> object:</p>

<p>Make a backup before editing your storagecluster:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc get storagecluster ocs-storagecluster <span class="nt">-oyaml</span> | <span class="nb">tee </span>storagecluster-ocs-storagecluster-before-remove-500g.yaml
</code></pre></div></div>

<p>change / edit your storagecluster storageDeviceSets so that only newly created storageDeviceSets remain; in my case, with 2TiB disks flavor:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc edit storagecluster ocs-storagecluster
...
  storageDeviceSets:
  - config: <span class="o">{}</span>
    count: 1
    dataPVCTemplate:
      metadata: <span class="o">{}</span>
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 2000Gi
        storageClassName: gp3-csi
        volumeMode: Block
      status: <span class="o">{}</span>
    name: ocs-deviceset-2t
    placement: <span class="o">{}</span>
    preparePlacement: <span class="o">{}</span>
    replica: 3
    resources:
      limits:
        cpu: <span class="s2">"2"</span>
        memory: 5Gi
      requests:
        cpu: <span class="s2">"2"</span>
        memory: 5Gi
  version: 4.10.0
</code></pre></div></div>

<h2 id="scale-up-openshift-data-foundation-operators">Scale up OpenShift Data Foundation operators</h2>
<p>At this point, you can scale up ocs-operator:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc scale deploy ocs-operator <span class="nt">--replicas</span><span class="o">=</span>1
deployment.apps/ocs-operator scaled
</code></pre></div></div>

<p>and then re-check Ceph health status:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ NAMESPACE</span><span class="o">=</span>openshift-storage<span class="p">;</span><span class="nv">ROOK_POD</span><span class="o">=</span><span class="si">$(</span>oc <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> get pod <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>rook-ceph-operator <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[0].metadata.name}'</span><span class="si">)</span><span class="p">;</span>oc <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">ROOK_POD</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--</span> ceph status <span class="nt">--cluster</span><span class="o">=</span><span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--conf</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>.config <span class="nt">--keyring</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/client.admin.keyring | egrep <span class="nt">-i</span> <span class="s1">'remapped|misplaced|active\+clean|HEALTH_OK|'</span>
  cluster:
    <span class="nb">id</span>:     .....
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum a,b,c <span class="o">(</span>age 3h<span class="o">)</span>
    mgr: a<span class="o">(</span>active, since 2w<span class="o">)</span>
    mds: 1/1 daemons up, 1 hot standby
    osd: 3 osds: 3 up <span class="o">(</span>since 7m<span class="o">)</span>, 3 <span class="k">in</span> <span class="o">(</span>since 6m<span class="o">)</span>
 
  data:
    volumes: 1/1 healthy
    pools:   4 pools, 193 pgs
    objects: 17.15k objects, 52 GiB
    usage:   145 GiB used, 5.7 TiB / 5.9 TiB avail
    pgs:     193 active+clean
 
  io:
    client:   853 B/s rd, 246 KiB/s wr, 1 op/s rd, 4 op/s wr
</code></pre></div></div>]]></content><author><name></name></author><category term="OpenShift" /><category term="OpenShift" /><category term="odf" /><summary type="html"><![CDATA[In this article, I’ll show you how to migrate your OpenShift Data Foundation OSDs (disks) from one flavor to another; in my case, I’ll migrate OSDs and data from 0.5TiB disks to 2TiB disks; this will be a “rolling” migration with no service or data disruption.]]></summary></entry><entry><title type="html">Using eBPF on OpenShift nodes (the quick and dirty way)</title><link href="/openshift/2023/01/22/Using-ebpf-on-OpenShift-the-quick-and-dirty-way.html" rel="alternate" type="text/html" title="Using eBPF on OpenShift nodes (the quick and dirty way)" /><published>2023-01-22T15:00:00+00:00</published><updated>2023-01-22T15:00:00+00:00</updated><id>/openshift/2023/01/22/Using-ebpf-on-OpenShift-the-quick-and-dirty-way</id><content type="html" xml:base="/openshift/2023/01/22/Using-ebpf-on-OpenShift-the-quick-and-dirty-way.html"><![CDATA[<p>In this <strong>OpenShift</strong> article, I’ll show you how to run <a href="https://iovisor.github.io/bcc/">bcc</a> tools, <a href="https://github.com/iovisor/bpftrace">bpftrace</a>, and the kernel tool <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/bpf/bpftool">bpftool</a>.</p>

<p><strong>Warning:</strong> If you are a Red Hat customer and you are in trouble, open a support case before going forward; otherwise, do the following steps at your own risk!</p>

<h2 id="requirements">Requirements</h2>
<p>Before starting, you’ll need:</p>

<ul>
  <li>Working OpenShift 4.10+ cluster, I’ve only tested this procedure on 4.10, 4.11, and 4.12 OpenShift clusters, but it may work on all supported 4.x versions (please let me know);</li>
  <li>cluster-admin grant on the OpenShift cluster;</li>
  <li>one subscribed RHEL host with <strong>podman</strong> and <strong>buildah</strong> installed or installable; You could use a non-subscribed host, but to use baseos and appstream eus repositories, you’d need to temporarily subscribe to the ubi8 container.</li>
  <li>SSH enabled on your OpenShift node(s).</li>
</ul>

<h2 id="run-must-gather">Run must-gather</h2>
<p>Before applying any change run an OpenShift must-gather:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc adm must-gather
</code></pre></div></div>

<h2 id="a-special-mention-to-openshift-412">A Special mention to OpenShift 4.12+</h2>
<p>Starting with OpenShift 4.12, the bpftool is included package in a toolbox and can be used without following this procedure:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ssh core@worker
<span class="o">[</span>core@worker-0 ~]<span class="nv">$ </span><span class="nb">sudo</span> <span class="nt">-i</span>

<span class="o">[</span>root@worker-0 ~]# toolbox 
Trying to pull registry.redhat.io/rhel8/support-tools:latest...
...
toolbox-root
Container started successfully. To <span class="nb">exit</span>, <span class="nb">type</span> <span class="s1">'exit'</span><span class="nb">.</span>
<span class="o">[</span>root@worker-0 /]#

<span class="o">[</span>root@worker-0 /]# bpftool <span class="nt">-h</span>
Usage: bpftool <span class="o">[</span>OPTIONS] OBJECT <span class="o">{</span> COMMAND | <span class="nb">help</span> <span class="o">}</span>
       bpftool batch file FILE
       bpftool version

       OBJECT :<span class="o">=</span> <span class="o">{</span> prog | map | <span class="nb">link</span> | cgroup | perf | net | feature | btf | gen | struct_ops | iter <span class="o">}</span>
       OPTIONS :<span class="o">=</span> <span class="o">{</span> <span class="o">{</span><span class="nt">-j</span>|--json<span class="o">}</span> <span class="o">[{</span><span class="nt">-p</span>|--pretty<span class="o">}]</span> | <span class="o">{</span><span class="nt">-d</span>|--debug<span class="o">}</span> |
                    <span class="o">{</span><span class="nt">-V</span>|--version<span class="o">}</span> <span class="o">}</span>
<span class="o">[</span>root@worker-0 /]#
</code></pre></div></div>

<p>but, if you want to run the bcc and bpftrace tools, you can continue to follow this guide.</p>

<h2 id="build-a-bpf-ocp-image-for-your-openshift-cluster">Build a bpf-ocp image for your OpenShift cluster</h2>
<p>Before running eBPF on your OpenShift nodes, you need to build a tailored image for your cluster.</p>

<p>This image will include:</p>

<ul>
  <li>kernel-core and kernel-headers for your OpenShift kernel version nodes;</li>
  <li>bpftrace, bcc, and bpftool packages;</li>
  <li>some other performance troubleshooting packages.</li>
</ul>

<h3 id="install-tools-on-rhel">Install tools on RHEL</h3>
<p>You need one host, usually a RHEL host, where you can build your new image with eBPF tools installed, in this example, I’ll show you how to install those tools on RHEL host.</p>

<p>Install buildah and podman:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>dnf <span class="nb">install </span>buildah podman <span class="nt">-y</span>
</code></pre></div></div>

<h3 id="retrieve-openshift-node-information--version">Retrieve OpenShift node information / version</h3>
<p>Remember to log in to your cluster and then choose a name for one of your ready nodes:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ OCPNODE</span><span class="o">=</span><span class="si">$(</span>oc get node | egrep <span class="s1">'\s+Ready\s+'</span> | <span class="nb">head</span> <span class="nt">-n1</span> | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>
<span class="nv">$ </span><span class="nb">echo</span> <span class="k">${</span><span class="nv">OCPNODE</span><span class="k">}</span>
master-0
</code></pre></div></div>

<p>obtain the node kernel version:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ KERNELVERSION</span><span class="o">=</span><span class="si">$(</span>oc debug node/<span class="k">${</span><span class="nv">OCPNODE</span><span class="k">}</span> <span class="nt">--</span> <span class="nb">chroot</span> /host <span class="nb">uname</span> <span class="nt">-r</span> 2&gt; /dev/null <span class="si">)</span>
<span class="nv">$ </span><span class="nb">echo</span> <span class="k">${</span><span class="nv">KERNELVERSION</span><span class="k">}</span> 
4.18.0-372.26.1.el8_6.x86_64
</code></pre></div></div>

<p>obtain the node RHEL minor version:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ RHEL8MINOR</span><span class="o">=</span><span class="si">$(</span>oc debug node/<span class="k">${</span><span class="nv">OCPNODE</span><span class="k">}</span> <span class="nt">--</span> <span class="nb">chroot</span> /host sh <span class="nt">-c</span> <span class="s2">"uname -r | sed -E 's/.+</span><span class="se">\.</span><span class="s2">el8_([0-9])</span><span class="se">\.</span><span class="s2">.*/</span><span class="se">\1</span><span class="s2">/g'"</span> 2&gt; /dev/null <span class="si">)</span>
<span class="nv">$ </span><span class="nb">echo</span> <span class="k">${</span><span class="nv">RHEL8MINOR</span><span class="k">}</span> 
6
</code></pre></div></div>

<h3 id="create-dockerfile">Create Dockerfile</h3>
<p>Create a Dockerfile for your image:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">mkdir </span>buildbpf
<span class="nv">$ </span><span class="nb">cd </span>buildbpf

<span class="nv">$ </span><span class="nb">tee</span> <span class="s2">"Dockerfile"</span> <span class="o">&gt;</span> /dev/null <span class="o">&lt;&lt;</span><span class="sh">'</span><span class="no">EOF</span><span class="sh">'
# Start from ubi8 with minor version RHEL8MINOR
FROM registry.access.redhat.com/ubi8:8.RHEL8MINOR
# Install some useful tools
RUN dnf install --disablerepo='*' --enablerepo=rhel-8-for-x86_64-baseos-eus-rpms --enablerepo=rhel-8-for-x86_64-appstream-eus-rpms --releasever=8.RHEL8MINOR </span><span class="se">\</span><span class="sh">
        redhat-lsb-core curl wget tcpdump vim iproute </span><span class="se">\</span><span class="sh">
        bind-utils sysstat procps-ng -y
# Install OCP node version of kernel-core and kernel-headers      
RUN dnf install --disablerepo='*' --enablerepo=rhel-8-for-x86_64-baseos-eus-rpms --enablerepo=rhel-8-for-x86_64-appstream-eus-rpms --releasever=8.RHEL8MINOR </span><span class="se">\</span><span class="sh">
        kernel-core-KERNELVERSION kernel-headers-KERNELVERSION -y
# Install bpftrace and bpftool, and their dependencies (bcc, python-bcc)
RUN dnf install --disablerepo='*' --enablerepo=rhel-8-for-x86_64-baseos-eus-rpms --enablerepo=rhel-8-for-x86_64-appstream-eus-rpms --releasever=8.RHEL8MINOR </span><span class="se">\</span><span class="sh">
        bpftrace bpftool -y
# Clean dnf cache
RUN dnf clean all
</span><span class="no">EOF
</span></code></pre></div></div>

<p>replace the RHEL 8 minor version and kernel version with your cluster versions:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sed</span> <span class="nt">-i</span> <span class="s2">"s/RHEL8MINOR/</span><span class="k">${</span><span class="nv">RHEL8MINOR</span><span class="k">}</span><span class="s2">/g"</span> Dockerfile
<span class="nv">$ </span><span class="nb">sed</span> <span class="nt">-i</span> <span class="s2">"s/KERNELVERSION/</span><span class="k">${</span><span class="nv">KERNELVERSION</span><span class="k">}</span><span class="s2">/g"</span> Dockerfile
</code></pre></div></div>

<p>review the content:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat </span>Dockerfile 
<span class="c"># Start from ubi8 with minor version 6</span>
FROM registry.access.redhat.com/ubi8:8.6
<span class="c"># Install some useful tools</span>
RUN dnf <span class="nb">install</span> <span class="nt">--disablerepo</span><span class="o">=</span><span class="s1">'*'</span> <span class="nt">--enablerepo</span><span class="o">=</span>rhel-8-for-x86_64-baseos-eus-rpms <span class="nt">--enablerepo</span><span class="o">=</span>rhel-8-for-x86_64-appstream-eus-rpms <span class="nt">--releasever</span><span class="o">=</span>8.6 <span class="se">\</span>
        redhat-lsb-core curl wget tcpdump vim iproute <span class="se">\</span>
        bind-utils sysstat procps-ng <span class="nt">-y</span>
<span class="c"># Install OCP node version of kernel-core and kernel-headers      </span>
RUN dnf <span class="nb">install</span> <span class="nt">--disablerepo</span><span class="o">=</span><span class="s1">'*'</span> <span class="nt">--enablerepo</span><span class="o">=</span>rhel-8-for-x86_64-baseos-eus-rpms <span class="nt">--enablerepo</span><span class="o">=</span>rhel-8-for-x86_64-appstream-eus-rpms <span class="nt">--releasever</span><span class="o">=</span>8.6 <span class="se">\</span>
        kernel-core-4.18.0-372.26.1.el8_6.x86_64 kernel-headers-4.18.0-372.26.1.el8_6.x86_64 <span class="nt">-y</span>
<span class="c"># Install bpftrace and bpftool, and their dependencies (bcc, python-bcc)</span>
RUN dnf <span class="nb">install</span> <span class="nt">--disablerepo</span><span class="o">=</span><span class="s1">'*'</span> <span class="nt">--enablerepo</span><span class="o">=</span>rhel-8-for-x86_64-baseos-eus-rpms <span class="nt">--enablerepo</span><span class="o">=</span>rhel-8-for-x86_64-appstream-eus-rpms <span class="nt">--releasever</span><span class="o">=</span>8.6 <span class="se">\</span>
        bpftrace bpftool <span class="nt">-y</span>
<span class="c"># Clean dnf cache</span>
RUN dnf clean all

</code></pre></div></div>

<p><strong>WARNING</strong>: the above content is an example! Your Dockerfile could have different versions!!!</p>

<h3 id="build-the-bpf-ocp-image">Build the bpf-ocp image</h3>
<p>Run buildah:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">export </span><span class="nv">BUILDAH_FORMAT</span><span class="o">=</span>docker

<span class="nv">$ </span>buildah bud <span class="nt">-t</span> bpf-ocp:8.<span class="k">${</span><span class="nv">RHEL8MINOR</span><span class="k">}</span>-<span class="k">${</span><span class="nv">KERNELVERSION</span><span class="k">}</span>
...
COMMIT bpf-ocp:8.6-4.18.0-372.26.1.el8_6.x86_64
Getting image <span class="nb">source </span>signatures
Copying blob b4e347eee7c8 skipped: already exists  
Copying blob 724516754461 <span class="k">done  
</span>Copying config 594a7e339c <span class="k">done  
</span>Writing manifest to image destination
Storing signatures
<span class="nt">--</span><span class="o">&gt;</span> 594a7e339cb
Successfully tagged localhost/bpf-ocp:8.6-4.18.0-372.26.1.el8_6.x86_64
594a7e339cb7ee321ad126c776012b29f4c5da2c8d302331906260d67e394ea2
<span class="err">$</span>
</code></pre></div></div>

<h3 id="special-case-for-not-rhel-subscribed-buildah-host">Special case for NOT RHEL subscribed buildah host</h3>
<p><strong>WARNING:</strong> Run these commands only if buildah bud has failed!!!</p>

<p>if your host is not a subscribed RHEL, you can run the following commands:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">rm </span>Dockerfile
<span class="nv">$ </span>buildah from registry.access.redhat.com/ubi8:8.<span class="k">${</span><span class="nv">RHEL8MINOR</span><span class="k">}</span>
ubi8-working-container
</code></pre></div></div>

<p>subscribe your container:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>buildah run ubi8-working-container  subscription-manager register
Registering to: subscription.rhsm.redhat.com:443/subscription
Username: &lt;YOUR RH ACCOUNT&gt;
Password: &lt;YOUR RH PASSWORD&gt;
The system has been registered with ID: 86326611-9b37-4888-b6bb-850007165594
The registered system name is: 84ff9ac8faa2
</code></pre></div></div>

<p>navigate to the <a href="https://access.redhat.com/management/">Red Hat Customer Portal</a>, click on <strong>Systems</strong>, then click on your container hostname (in my case, 84ff9ac8faa2), select <strong>Subscriptions</strong>, click on the <strong>Attach Subscriptions</strong> button, Select the subscription you want in the left check box, then click <strong>Attach Subscriptions</strong>.</p>

<p>go back to the terminal and run:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>buildah run ubi8-working-container  subscription-manager repos <span class="nt">--list</span> | <span class="nb">tee</span> <span class="nt">-a</span> /tmp/repos.txt

<span class="nv">$ </span>buildah run ubi8-working-container dnf <span class="nb">install</span> <span class="se">\</span>
        <span class="nt">--disablerepo</span><span class="o">=</span><span class="s1">'*'</span> <span class="nt">--enablerepo</span><span class="o">=</span>rhel-8-for-x86_64-baseos-eus-rpms <span class="se">\</span>
        <span class="nt">--enablerepo</span><span class="o">=</span>rhel-8-for-x86_64-appstream-eus-rpms <span class="nt">--releasever</span><span class="o">=</span>8.<span class="k">${</span><span class="nv">RHEL8MINOR</span><span class="k">}</span> <span class="se">\</span>
        redhat-lsb-core curl wget tcpdump vim iproute <span class="se">\</span>
        bind-utils sysstat procps-ng <span class="nt">-y</span>

<span class="nv">$ </span>buildah run ubi8-working-container dnf <span class="nb">install</span> <span class="nt">--disablerepo</span><span class="o">=</span><span class="s1">'*'</span> <span class="se">\</span>
        <span class="nt">--enablerepo</span><span class="o">=</span>rhel-8-for-x86_64-baseos-eus-rpms <span class="se">\</span>
        <span class="nt">--enablerepo</span><span class="o">=</span>rhel-8-for-x86_64-appstream-eus-rpms <span class="nt">--releasever</span><span class="o">=</span>8.<span class="k">${</span><span class="nv">RHEL8MINOR</span><span class="k">}</span> <span class="se">\</span>
        kernel-core-<span class="k">${</span><span class="nv">KERNELVERSION</span><span class="k">}</span> kernel-headers-<span class="k">${</span><span class="nv">KERNELVERSION</span><span class="k">}</span> <span class="nt">-y</span>

<span class="nv">$ </span>buildah run ubi8-working-container dnf <span class="nb">install</span> <span class="nt">--disablerepo</span><span class="o">=</span><span class="s1">'*'</span> <span class="se">\</span>
        <span class="nt">--enablerepo</span><span class="o">=</span>rhel-8-for-x86_64-baseos-eus-rpms <span class="se">\</span>
        <span class="nt">--enablerepo</span><span class="o">=</span>rhel-8-for-x86_64-appstream-eus-rpms <span class="nt">--releasever</span><span class="o">=</span>8.<span class="k">${</span><span class="nv">RHEL8MINOR</span><span class="k">}</span> <span class="se">\</span>
        bpftrace bpftool <span class="nt">-y</span>

<span class="nv">$ </span>buildah run ubi8-working-container dnf clean all

<span class="nv">$ </span>buildah run ubi8-working-container subscription-manager unregister

<span class="nv">$ </span>buildah run ubi8-working-container subscription-manager clean
</code></pre></div></div>

<p>execute buildah commit:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>buildah commit ubi8-working-container bpf-ocp:8.<span class="k">${</span><span class="nv">RHEL8MINOR</span><span class="k">}</span>-<span class="k">${</span><span class="nv">KERNELVERSION</span><span class="k">}</span>
</code></pre></div></div>

<p>remove the buildah image:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>buildah <span class="nb">rm </span>ubi8-working-container
</code></pre></div></div>

<p>check image:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>podman image <span class="nb">ls</span> | egrep <span class="s1">'^REPOSI|bpf-ocp'</span>
REPOSITORY                                                               TAG                               IMAGE ID      CREATED             SIZE
localhost/bpf-ocp                                                        8.6-4.18.0-372.26.1.el8_6.x86_64  30712fe599dd  About a minute ago  1.54 GB
</code></pre></div></div>

<h3 id="save-the-image-as-a-tar-file">Save the image as a tar file</h3>
<p>Save the just created bpf-ocp image as a tar file:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>podman save <span class="nt">--quiet</span> <span class="nt">--format</span> docker-archive <span class="se">\</span>
    <span class="nt">-o</span> bpf-ocp-8.<span class="k">${</span><span class="nv">RHEL8MINOR</span><span class="k">}</span>-<span class="k">${</span><span class="nv">KERNELVERSION</span><span class="k">}</span>.tar <span class="se">\</span>
    localhost/bpf-ocp:8.<span class="k">${</span><span class="nv">RHEL8MINOR</span><span class="k">}</span>-<span class="k">${</span><span class="nv">KERNELVERSION</span><span class="k">}</span>
</code></pre></div></div>

<h3 id="transfer-bpf-ocp-image-to-desired-openshift-node">Transfer bpf-ocp image to desired OpenShift node</h3>
<p>In this example, I want to run bpf tools on the <strong>worker-1</strong> node, but change this to your real OpenShift node.</p>

<p>First, obtain the IP address of the node:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ IPNODE</span><span class="o">=</span><span class="si">$(</span>oc get node <span class="nt">-owide</span> | <span class="nb">grep </span>worker-1 | <span class="nb">awk</span> <span class="s1">'{print $6}'</span><span class="si">)</span>
<span class="nv">$ </span><span class="nb">echo</span> <span class="k">${</span><span class="nv">IPNODE</span><span class="k">}</span> 
192.168.203.57
</code></pre></div></div>

<p>transfer image using scp:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>scp bpf-ocp-8.<span class="k">${</span><span class="nv">RHEL8MINOR</span><span class="k">}</span>-<span class="k">${</span><span class="nv">KERNELVERSION</span><span class="k">}</span>.tar <span class="se">\</span>
      core@<span class="k">${</span><span class="nv">IPNODE</span><span class="k">}</span>:/tmp/bpf-ocp-8.<span class="k">${</span><span class="nv">RHEL8MINOR</span><span class="k">}</span>-<span class="k">${</span><span class="nv">KERNELVERSION</span><span class="k">}</span>.tar
bpf-ocp-8.6-4.18.0-372.26.1.el8_6.x86_64.tar 100% 1464MB 109.9MB/s   00:13
</code></pre></div></div>

<h3 id="load-image-from-tar-file">Load image from tar file</h3>
<p>If you’re running OpenShift 4.11+, you can simply run:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ssh core@<span class="k">${</span><span class="nv">IPNODE</span><span class="k">}</span>
<span class="o">[</span>core@worker-1 ~]<span class="nv">$ </span><span class="nb">sudo</span> <span class="nt">-i</span>
<span class="o">[</span>root@worker-1 ~]# 

<span class="o">[</span>root@worker-1 ~]# podman load <span class="nt">--input</span> /tmp/bpf-ocp-8.6-4.18.0-372.26.1.el8_6.x86_64.tar
Getting image <span class="nb">source </span>signatures
Copying blob 724516754461 <span class="k">done
</span>Copying blob b4e347eee7c8 <span class="k">done
</span>Copying config 594a7e339c <span class="k">done
</span>Writing manifest to image destination
Storing signatures
Loaded image<span class="o">(</span>s<span class="o">)</span>: localhost/bpf-ocp:8.6-4.18.0-372.26.1.el8_6.x86_64
<span class="o">[</span>root@worker-1 ~]#
</code></pre></div></div>

<p>Instead, if you’re trying to load image on OpenShift 4.10 with podman 3.x / CoreOS 8.4, you can get this error (loglevel debug):</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@worker-1 ~]# podman load <span class="nt">--log-level</span><span class="o">=</span>debug <span class="nt">-i</span> /tmp/bpf-ocp-8.4-4.18.0-305.65.1.el8_4.x86_64.tar
...
DEBU[0001] Error loading /tmp/bpf-ocp-8.4-4.18.0-305.65.1.el8_4.x86_64.tar: Source image rejected: Running image docker-archive:/tmp/bpf-ocp-8.4-4.18.0-305.65.1.el8_4.x86_64.tar:localhost/bpf-ocp:8.4-4.18.0-305.65.1.el8_4.x86_64 is rejected by policy.
</code></pre></div></div>

<p>in this case, create a permissive policy file:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@worker-1 ~]# <span class="nb">echo</span> <span class="s1">'{ "default": [{"type": "insecureAcceptAnything"}]}'</span> <span class="o">&gt;</span> /tmp/policy-permissive.json
</code></pre></div></div>

<p>and use this permissive signature file in order to load the image:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@worker-1 ~]# podman load <span class="nt">--signature-policy</span> /tmp/policy-permissive.json <span class="nt">-i</span> /tmp/bpf-ocp-8.4-4.18.0-305.65.1.el8_4.x86_64.tar 
Getting image <span class="nb">source </span>signatures
Copying blob d46291327397 <span class="k">done  
</span>Copying blob 5bc03dec6239 <span class="k">done  
</span>Copying blob 525ed45dbdb1 <span class="k">done  
</span>Copying config 17ae9469bd <span class="k">done  
</span>Writing manifest to image destination
Storing signatures
Loaded image<span class="o">(</span>s<span class="o">)</span>: localhost/bpf-ocp:8.4-4.18.0-305.65.1.el8_4.x86_64
</code></pre></div></div>

<h3 id="run-the-bpf-ocp-container">Run the bpf-ocp container</h3>
<p>Finally, you can spin up a new bpf-ocp container:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@worker-1 ~]# podman run <span class="nt">--privileged</span> <span class="nt">--name</span> bpf-ocp <span class="se">\</span>
    <span class="nt">--mount</span> <span class="nb">type</span><span class="o">=</span><span class="nb">bind</span>,source<span class="o">=</span>/sys/kernel/debug,target<span class="o">=</span>/sys/kernel/debug <span class="se">\</span>
    <span class="nt">-it</span> localhost/bpf-ocp:8.6-4.18.0-372.26.1.el8_6.x86_64 
<span class="o">[</span>root@d18d4f16d28a /]#
</code></pre></div></div>

<p>and test a bcc tool biolatency in order to see if it is working properly (press Ctrl-C to end tracing):</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@d18d4f16d28a /]# /usr/share/bcc/tools/biolatency
Tracing block device I/O... Hit Ctrl-C to end.
^C
     usecs               : count     distribution
         0 -&gt; 1          : 0        |                                        |
         2 -&gt; 3          : 0        |                                        |
         4 -&gt; 7          : 0        |                                        |
         8 -&gt; 15         : 0        |                                        |
        16 -&gt; 31         : 7        |                                        |
        32 -&gt; 63         : 386      |<span class="k">**************************</span>              |
        64 -&gt; 127        : 452      |<span class="k">******************************</span>          |
       128 -&gt; 255        : 585      |<span class="k">****************************************</span>|
       256 -&gt; 511        : 501      |<span class="k">**********************************</span>      |
       512 -&gt; 1023       : 161      |<span class="k">***********</span>                             |
      1024 -&gt; 2047       : 18       |<span class="k">*</span>                                       |
      2048 -&gt; 4095       : 58       |<span class="k">***</span>                                     |
      4096 -&gt; 8191       : 56       |<span class="k">***</span>                                     |
      8192 -&gt; 16383      : 55       |<span class="k">***</span>                                     |
     16384 -&gt; 32767      : 14       |                                        |
     32768 -&gt; 65535      : 3        |                                        |
     65536 -&gt; 131071     : 5        |                                        |
<span class="o">[</span>root@d18d4f16d28a /]#
</code></pre></div></div>

<h3 id="conclusion">Conclusion</h3>
<p>This is a quick and dirty way to run eBPF on your OpenShift cluster, but you can build your bpf-ocp image for your Cluster(s), publish it / them to your registry, and for example, deploy it as DaemonSet on all your cluster nodes.</p>]]></content><author><name></name></author><category term="OpenShift" /><category term="OpenShift" /><category term="ebpf" /><summary type="html"><![CDATA[In this OpenShift article, I’ll show you how to run bcc tools, bpftrace, and the kernel tool bpftool.]]></summary></entry><entry><title type="html">Migrate Elasticsearch shards to a new StorageClass</title><link href="/openshift/2023/01/08/Migrate-elastic-data-to-a-new-storageclass.html" rel="alternate" type="text/html" title="Migrate Elasticsearch shards to a new StorageClass" /><published>2023-01-08T09:00:00+00:00</published><updated>2023-01-08T09:00:00+00:00</updated><id>/openshift/2023/01/08/Migrate-elastic-data-to-a-new-storageclass</id><content type="html" xml:base="/openshift/2023/01/08/Migrate-elastic-data-to-a-new-storageclass.html"><![CDATA[<p>In this <strong>OpenShift</strong> / <strong>Kubernetes</strong> article, I’ll show you how to migrate your <strong>Elasticsearch</strong> data (shards), from one cloud StorageClass, such as Azure managed-premium, to another cloud StorageClass, such as Azure <strong>managed-csi</strong>, this will be a “rolling” migration, with no service and data disruption.</p>

<p><strong>Warning:</strong> If you are a Red Hat customer, open a support case before going forward; otherwise do the following steps at your own risk!</p>

<h2 id="requirements">Requirements</h2>
<p>Before starting you’ll need:</p>

<ul>
  <li>Elasticsearch is installed and operational[0];</li>
  <li>configured new / destination Storage Class =&gt; in this article I’ll use <strong>managed-csi</strong>;</li>
  <li>Elasticsearch running on three different worker nodes (typically from three different availability zones);</li>
  <li>Elasticsearch is currently working on three different CDMs, each with its own PVC on an old StorageClass (in my case, Azure managed-premium) [1];</li>
  <li>Elasticsearch is configured with minimum_master_nodes set to 2;</li>
</ul>

<p>[0] In this guide, I’ll assume you’ve installed Elasticsearch on a hyperscale cloud provider, such as Azure or AWS, with three availability zones.</p>

<p>[1] Three elasticsearch CDMs running on OpenShift as examples:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc get pods <span class="nt">-l</span> <span class="nv">component</span><span class="o">=</span>elasticsearch <span class="nt">-o</span> wide <span class="nt">-n</span> openshift-logging
NAME                                            READY   STATUS    RESTARTS   AGE   IP            NODE                                          NOMINATED NODE   READINESS GATES
elasticsearch-cdm-19ibb0br-1-f58b8f764-6dnvg    2/2     Running   0          42d   100.65.8.8    nodename-xxxx-elastic-northeurope3-vk7cf   &lt;none&gt;           &lt;none&gt;
elasticsearch-cdm-19ibb0br-2-787fd9c4c5-r88lc   2/2     Running   0          41d   100.65.6.8    nodename-xxxx-elastic-northeurope1-rd545   &lt;none&gt;           &lt;none&gt;
elasticsearch-cdm-19ibb0br-3-6bc8c8f98-w6jh9    2/2     Running   0          42d   100.65.10.8   nodename-xxxx-elastic-northeurope2-mbtc9   &lt;none&gt;           &lt;none&gt;
</code></pre></div></div>

<h2 id="run-must-gather">Run must-gather</h2>
<p>Before applying any change run an OpenShift must-gather:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc adm must-gather
</code></pre></div></div>

<p>then, create a specific cluster-logging must-gather:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc adm must-gather <span class="nt">--image</span><span class="o">=</span><span class="si">$(</span>oc <span class="nt">-n</span> openshift-logging get deployment.apps/cluster-logging-operator <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.spec.template.spec.containers[?(@.name == "cluster-logging-operator")].image}'</span><span class="si">)</span>
</code></pre></div></div>

<h2 id="check-cluster-health--green-state">Check cluster health / green state</h2>
<p>Check if your cluster is in green state:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc project openshift-logging
<span class="nv">$ </span><span class="nb">export </span><span class="nv">ELKCDM</span><span class="o">=</span><span class="si">$(</span>oc get pods <span class="nt">-l</span> <span class="nv">component</span><span class="o">=</span>elasticsearch <span class="nt">-o</span> wide | egrep <span class="s1">'2\/2\s+Running'</span> | <span class="nb">head</span> <span class="nt">-n1</span> | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>

<span class="nv">$ </span>oc <span class="nb">exec</span> <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">-c</span> elasticsearch <span class="nt">--</span> health | egrep <span class="s1">'green|'</span>
epoch      timestamp cluster       status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1661176875 14:01:15  elasticsearch green           3         3    428 214    0    0        0             0                  -                100.0%
</code></pre></div></div>

<p><strong>WARNING:</strong> if your cluster is not in <strong>green</strong>, and <strong>active_shards_percent</strong> is not equal to 100%, stop any activities and check the elasticsearch state first!</p>

<h2 id="check-cluster-routing-allocation-parameter">Check cluster routing allocation parameter</h2>
<p>The cluster.routing.allocation.enable parameter must be “all”, for example, if you have “primaries”, you need to change it to “all” and wait for shards creation / relocation.</p>

<p>This is the correct value:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc <span class="nb">exec</span> <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">-c</span> elasticsearch <span class="nt">--</span> es_util <span class="nt">--query</span><span class="o">=</span>_cluster/settings?pretty
<span class="o">{</span>
  <span class="s2">"persistent"</span> : <span class="o">{</span>
    <span class="s2">"cluster"</span> : <span class="o">{</span>
      <span class="s2">"routing"</span> : <span class="o">{</span>
        <span class="s2">"allocation"</span> : <span class="o">{</span>
          <span class="s2">"enable"</span> : <span class="s2">"all"</span>
        <span class="o">}</span>
      <span class="o">}</span>
    <span class="o">}</span>,
    <span class="s2">"discovery"</span> : <span class="o">{</span>
      <span class="s2">"zen"</span> : <span class="o">{</span>
        <span class="s2">"minimum_master_nodes"</span> : <span class="s2">"2"</span>
      <span class="o">}</span>
    <span class="o">}</span>
  <span class="o">}</span>,
  <span class="s2">"transient"</span> : <span class="o">{</span> <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>instead, if you have primaries:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc <span class="nb">exec</span> <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">-c</span> elasticsearch <span class="nt">--</span> es_util <span class="nt">--query</span><span class="o">=</span>_cluster/settings?pretty
<span class="o">{</span>
  <span class="s2">"persistent"</span> : <span class="o">{</span>
    <span class="s2">"cluster"</span> : <span class="o">{</span>
      <span class="s2">"routing"</span> : <span class="o">{</span>
        <span class="s2">"allocation"</span> : <span class="o">{</span>
          <span class="s2">"enable"</span> : <span class="s2">"primaries"</span>
        <span class="o">}</span>
      <span class="o">}</span>
    <span class="o">}</span>,
    <span class="s2">"discovery"</span> : <span class="o">{</span>
      <span class="s2">"zen"</span> : <span class="o">{</span>
        <span class="s2">"minimum_master_nodes"</span> : <span class="s2">"2"</span>
      <span class="o">}</span>
    <span class="o">}</span>
  <span class="o">}</span>,
  <span class="s2">"transient"</span> : <span class="o">{</span> <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>in this case, you have to overwrite to all:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc <span class="nb">exec</span> <span class="nt">-c</span> elasticsearch <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">--</span> curl <span class="nt">-s</span> <span class="nt">--key</span> /etc/elasticsearch/secret/admin-key <span class="nt">--cert</span> /etc/elasticsearch/secret/admin-cert <span class="nt">--cacert</span> /etc/elasticsearch/secret/admin-ca <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="nt">-XPUT</span> <span class="s2">"https://localhost:9200/_cluster/settings"</span> <span class="nt">-d</span> <span class="s1">'{ "persistent":{ "cluster.routing.allocation.enable" : "all" }}'</span>
<span class="o">{</span><span class="s2">"acknowledged"</span>:true,<span class="s2">"persistent"</span>:<span class="o">{</span><span class="s2">"cluster"</span>:<span class="o">{</span><span class="s2">"routing"</span>:<span class="o">{</span><span class="s2">"allocation"</span>:<span class="o">{</span><span class="s2">"enable"</span>:<span class="s2">"all"</span><span class="o">}}}}</span>,<span class="s2">"transient"</span>:<span class="o">{}}</span>

<span class="nv">$ </span>oc <span class="nb">exec</span> <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">-c</span> elasticsearch <span class="nt">--</span> es_util <span class="nt">--query</span><span class="o">=</span>_cluster/settings?pretty
<span class="o">{</span>
  <span class="s2">"persistent"</span> : <span class="o">{</span>
    <span class="s2">"cluster"</span> : <span class="o">{</span>
      <span class="s2">"routing"</span> : <span class="o">{</span>
        <span class="s2">"allocation"</span> : <span class="o">{</span>
          <span class="s2">"enable"</span> : <span class="s2">"all"</span>
        <span class="o">}</span>
      <span class="o">}</span>
    <span class="o">}</span>,
    <span class="s2">"discovery"</span> : <span class="o">{</span>
      <span class="s2">"zen"</span> : <span class="o">{</span>
        <span class="s2">"minimum_master_nodes"</span> : <span class="s2">"2"</span>
      <span class="o">}</span>
    <span class="o">}</span>
  <span class="o">}</span>,
  <span class="s2">"transient"</span> : <span class="o">{</span> <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>then wait until the relocation column (relo column) reaches zero (0)</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="k">while </span><span class="nb">true</span> <span class="p">;</span> <span class="k">do </span>oc <span class="nb">exec</span> <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">-c</span> elasticsearch <span class="nt">--</span> health | egrep <span class="s1">'green\s+3\s+3|'</span> <span class="p">;</span> <span class="nb">sleep </span>10 <span class="p">;</span> <span class="k">done
</span>epoch      timestamp cluster       status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1661259701 13:01:41  elasticsearch green           3         3    428 214    2    0        0             0                  -                100.0%
...........
</code></pre></div></div>

<h2 id="check-for-shards-not-started">Check for shards not started</h2>
<p>Check that all your shards are in STARTED state:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span> oc <span class="nb">exec</span> <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">-c</span> elasticsearch <span class="nt">--</span> es_util <span class="nt">--query</span><span class="o">=</span>_cat/shards?v | <span class="nb">grep</span> <span class="nt">-v</span> STARTED
index                          shard prirep state      docs   store ip          node
</code></pre></div></div>

<p><strong>Warning:</strong> the above command must return only one line, which is the column header; if you have some shards that are NOT in STARTED state, stop any activities and check first the elasticsearch state!</p>

<h2 id="replace-the-storageclass-in-the-clusterlogging-instance">Replace the StorageClass in the clusterlogging instance</h2>
<p>Edit your clusterlogging instance with your new StorageClass name (in my case, managed-csi).</p>

<p>Backup it before editing:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc get clusterlogging instance <span class="nt">-oyaml</span> | <span class="nb">tee</span> <span class="nt">-a</span> clusterlogging-instance-before-storageclass-change.yaml
</code></pre></div></div>

<p>then edit it changing only the storageClassName parameter:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc edit clusterlogging instance
clusterlogging.logging.openshift.io/instance edited
</code></pre></div></div>

<p>check for proper storageClassName value:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc get clusterlogging instance <span class="nt">-ojson</span> | jq <span class="nt">-r</span> <span class="s1">'.spec.logStore.elasticsearch.storage.storageClassName'</span>
managed-csi
</code></pre></div></div>

<h2 id="remove-shards-from-one-elastic-cdm-pod">Remove shards from one elastic CDM pod</h2>
<p>Now, you can identify one elastic CDM and its overlay IP in order to relocate all shards from it:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span> oc <span class="nb">exec</span> <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">-c</span> elasticsearch <span class="nt">--</span> es_util <span class="nt">--query</span><span class="o">=</span>_cat/nodes?v
ip          heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
100.65.8.8            48          99  15    2.36    2.21     2.40 mdi       -      elasticsearch-cdm-19ibb0br-1
100.65.6.8            26          99  10    0.90    1.32     1.72 mdi       <span class="k">*</span>      elasticsearch-cdm-19ibb0br-2
100.65.10.8           24          99  18    2.21    2.63     3.30 mdi       -      elasticsearch-cdm-19ibb0br-3
</code></pre></div></div>

<p>In this example, I’ll move shards from CDM-1 <strong>elasticsearch-cdm-19ibb0br-1</strong>, which has the IP address <strong>100.65.8.8</strong></p>

<h3 id="exclude-cdm-ip">Exclude CDM IP</h3>
<p>To move shards from an identified elasticsearch CDM, you must exclude its IP, in my case 100.65.8.8, but you must change the IP of your CDM:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span> oc <span class="nb">exec</span> <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">-c</span> elasticsearch <span class="nt">--</span> es_util <span class="nt">--query</span><span class="o">=</span>_cluster/settings?pretty <span class="nt">-X</span> PUT <span class="nt">-d</span> <span class="s1">'{"transient":{"cluster.routing.allocation.exclude._ip": "100.65.8.8"}}'</span>
<span class="o">{</span>
  <span class="s2">"acknowledged"</span> : <span class="nb">true</span>,
  <span class="s2">"persistent"</span> : <span class="o">{</span> <span class="o">}</span>,
  <span class="s2">"transient"</span> : <span class="o">{</span>
    <span class="s2">"cluster"</span> : <span class="o">{</span>
      <span class="s2">"routing"</span> : <span class="o">{</span>
        <span class="s2">"allocation"</span> : <span class="o">{</span>
          <span class="s2">"exclude"</span> : <span class="o">{</span>
            <span class="s2">"_ip"</span> : <span class="s2">"100.65.8.8"</span>
          <span class="o">}</span>
        <span class="o">}</span>
      <span class="o">}</span>
    <span class="o">}</span>
  <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>wait until all shards are relocated to the other two elasticsearch nodes:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc rsh <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> 
Defaulted container <span class="s2">"elasticsearch"</span> out of: elasticsearch, proxy
sh-4.4<span class="nv">$ </span><span class="k">while </span><span class="nb">true</span> <span class="p">;</span> <span class="k">do </span>es_util <span class="nt">--query</span><span class="o">=</span>_cat/shards?v | <span class="nb">grep </span>100.65.8.8 | <span class="nb">wc</span> <span class="nt">-l</span> <span class="p">;</span> <span class="nb">sleep </span>10 <span class="p">;</span> <span class="k">done
</span>142
140
... <span class="o">(</span><span class="nb">cut</span><span class="o">)</span>
0
<span class="o">(</span>Ctrl-c<span class="o">)</span>
sh-4.4<span class="nv">$ </span><span class="nb">exit</span>
</code></pre></div></div>
<h3 id="scale-elasticsearch-cdm-deploy-to-zero">Scale elasticsearch CDM deploy to zero</h3>
<p>Scale to zero (0) elasticsearch CDM deploy, in my case elasticsearch-cdm-19ibb0br-1</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc get deploy
NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
cluster-logging-operator       1/1     1            1           137d
elasticsearch-cdm-19ibb0br-1   1/1     1            1           137d
elasticsearch-cdm-19ibb0br-2   1/1     1            1           137d
elasticsearch-cdm-19ibb0br-3   1/1     1            1           137d
kibana                         1/1     1            1           89d

<span class="nv">$ </span>oc scale deploy elasticsearch-cdm-19ibb0br-1 <span class="nt">--replicas</span><span class="o">=</span>0
deployment.apps/elasticsearch-cdm-19ibb0br-1 scaled
</code></pre></div></div>

<h3 id="delete-elasticsearch-cdm-pvc">Delete elasticsearch CDM PVC</h3>
<p>Delete the PVC corresponding to your elasticsearch CDM, in my case <strong>elasticsearch-elasticsearch-cdm-19ibb0br-1</strong> but you need to change with your pvc:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc get pvc
NAME                                         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS       AGE
elasticsearch-elasticsearch-cdm-19ibb0br-1   Bound    pvc-3d378adc-901a-4198-9ff0-f720d32eaa4d   500Gi      RWO            managed-premium    137d
elasticsearch-elasticsearch-cdm-19ibb0br-2   Bound    pvc-318f2d40-2580-486d-a6ac-cb1822427fd3   500Gi      RWO            managed-premium    137d
elasticsearch-elasticsearch-cdm-19ibb0br-3   Bound    pvc-7052bed6-09aa-4789-b86e-9d68616b6401   500Gi      RWO            managed-premium    137d

<span class="nv">$ </span>oc delete pvc elasticsearch-elasticsearch-cdm-19ibb0br-1
persistentvolumeclaim <span class="s2">"elasticsearch-elasticsearch-cdm-19ibb0br-1"</span> deleted

<span class="nv">$ </span>oc get pvc
NAME                                         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS       AGE
elasticsearch-elasticsearch-cdm-19ibb0br-2   Bound    pvc-318f2d40-2580-486d-a6ac-cb1822427fd3   500Gi      RWO            managed-premium    137d
elasticsearch-elasticsearch-cdm-19ibb0br-3   Bound    pvc-7052bed6-09aa-4789-b86e-9d68616b6401   500Gi      RWO            managed-premium    137d
</code></pre></div></div>

<h3 id="check-cluster-health--green-state-1">Check cluster health / green state</h3>
<p>Check if your cluster is still in green state:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">export </span><span class="nv">ELKCDM</span><span class="o">=</span><span class="si">$(</span>oc get pods <span class="nt">-l</span> <span class="nv">component</span><span class="o">=</span>elasticsearch <span class="nt">-o</span> wide | egrep <span class="s1">'2\/2\s+Running'</span> | <span class="nb">head</span> <span class="nt">-n1</span> | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>
<span class="nv">$ </span>oc <span class="nb">exec</span> <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">-c</span> elasticsearch <span class="nt">--</span> health | egrep <span class="s1">'green|'</span>
epoch      timestamp cluster       status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1661258957 12:49:17  elasticsearch green           2         2    416 208    0    0        0             0                  -                100.0%
</code></pre></div></div>

<p><strong>WARNING:</strong> if your cluster is not in <strong>green</strong>, and <strong>active_shards_percent</strong> is not equal to 100%, stop any activities and check the elasticsearch state first!</p>

<h3 id="remove-exclude-ip">Remove exclude IP</h3>
<p>Remove your previously exclude ip parameter:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc <span class="nb">exec</span> <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">-c</span> elasticsearch <span class="nt">--</span> es_util <span class="nt">--query</span><span class="o">=</span>_cluster/settings?pretty <span class="nt">-X</span> PUT <span class="nt">-d</span> <span class="s1">'{"transient":{"cluster.routing.allocation.exclude._ip" : null}}'</span>
<span class="o">{</span>
  <span class="s2">"acknowledged"</span> : <span class="nb">true</span>,
  <span class="s2">"persistent"</span> : <span class="o">{</span> <span class="o">}</span>,
  <span class="s2">"transient"</span> : <span class="o">{</span> <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="scale-elasticsearch-cdm-deploy-to-one">Scale elasticsearch CDM deploy to one</h3>
<p>Scale back to one (1) elasticsearch CDM deploy, in my case, elasticsearch-cdm-19ibb0br-1</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc get deploy
NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
cluster-logging-operator       1/1     1            1           137d
elasticsearch-cdm-19ibb0br-1   0/0     0            0           137d
elasticsearch-cdm-19ibb0br-2   1/1     1            1           137d
elasticsearch-cdm-19ibb0br-3   1/1     1            1           137d
kibana                         1/1     1            1           89d

<span class="nv">$ </span>oc scale deploy elasticsearch-cdm-19ibb0br-1 <span class="nt">--replicas</span><span class="o">=</span>1
deployment.apps/elasticsearch-cdm-19ibb0br-1 scaled
</code></pre></div></div>
<p>check if there are 3 nodes:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc <span class="nb">exec</span> <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">-c</span> elasticsearch <span class="nt">--</span> health | egrep <span class="s1">'green\s+3\s+3|'</span>
Tue Aug 23 12:58:52 UTC 2022
epoch      timestamp cluster       status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1661259532 12:58:52  elasticsearch green           3         3    416 208    2    0        0             0                  -                100.0%
</code></pre></div></div>

<h3 id="re-set-cluster-routing-to-all">Re-set cluster routing to all</h3>
<p>Re-set cluster.routing.allocation.enable parameter to all:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc <span class="nb">exec</span> <span class="nt">-c</span> elasticsearch <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">--</span> curl <span class="nt">-s</span> <span class="nt">--key</span> /etc/elasticsearch/secret/admin-key <span class="nt">--cert</span> /etc/elasticsearch/secret/admin-cert <span class="nt">--cacert</span> /etc/elasticsearch/secret/admin-ca <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="nt">-XPUT</span> <span class="s2">"https://localhost:9200/_cluster/settings"</span> <span class="nt">-d</span> <span class="s1">'{ "persistent":{ "cluster.routing.allocation.enable" : "all" }}'</span>
<span class="o">{</span><span class="s2">"acknowledged"</span>:true,<span class="s2">"persistent"</span>:<span class="o">{</span><span class="s2">"cluster"</span>:<span class="o">{</span><span class="s2">"routing"</span>:<span class="o">{</span><span class="s2">"allocation"</span>:<span class="o">{</span><span class="s2">"enable"</span>:<span class="s2">"all"</span><span class="o">}}}}</span>,<span class="s2">"transient"</span>:<span class="o">{}}</span>

<span class="nv">$ </span>oc <span class="nb">exec</span> <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">-c</span> elasticsearch <span class="nt">--</span> es_util <span class="nt">--query</span><span class="o">=</span>_cluster/settings?pretty
<span class="o">{</span>
  <span class="s2">"persistent"</span> : <span class="o">{</span>
    <span class="s2">"cluster"</span> : <span class="o">{</span>
      <span class="s2">"routing"</span> : <span class="o">{</span>
        <span class="s2">"allocation"</span> : <span class="o">{</span>
          <span class="s2">"enable"</span> : <span class="s2">"all"</span>
        <span class="o">}</span>
      <span class="o">}</span>
    <span class="o">}</span>,
    <span class="s2">"discovery"</span> : <span class="o">{</span>
      <span class="s2">"zen"</span> : <span class="o">{</span>
        <span class="s2">"minimum_master_nodes"</span> : <span class="s2">"2"</span>
      <span class="o">}</span>
    <span class="o">}</span>
  <span class="o">}</span>,
  <span class="s2">"transient"</span> : <span class="o">{</span> <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>and then wait for relocation:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="k">while </span><span class="nb">true</span> <span class="p">;</span> <span class="k">do </span>oc <span class="nb">exec</span> <span class="k">${</span><span class="nv">ELKCDM</span><span class="k">}</span> <span class="nt">-c</span> elasticsearch <span class="nt">--</span> health | egrep <span class="s1">'green\s+3\s+3|'</span> <span class="p">;</span> <span class="nb">sleep </span>10 <span class="p">;</span> <span class="k">done
</span>epoch      timestamp cluster       status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1661259701 13:01:41  elasticsearch green           3         3    428 214    2    0        0             0                  -                100.0%
</code></pre></div></div>

<h2 id="repeat-the-previous-steps-for-the-remaining-elasticsearch-cdm">Repeat the previous steps for the remaining elasticsearch CDM</h2>
<p>Repeat all the steps from “Remove shards from one elastic CDM pod” to “Re-set cluster routing to all” for all the remaining elasticsearch CDM.</p>]]></content><author><name></name></author><category term="OpenShift" /><category term="OpenShift" /><category term="elasticsearch" /><category term="elk" /><summary type="html"><![CDATA[In this OpenShift / Kubernetes article, I’ll show you how to migrate your Elasticsearch data (shards), from one cloud StorageClass, such as Azure managed-premium, to another cloud StorageClass, such as Azure managed-csi, this will be a “rolling” migration, with no service and data disruption.]]></summary></entry><entry><title type="html">Migrate OSDs from one storage class to another</title><link href="/openshift/2022/11/28/Migrate-ODF-osd-storageclass.html" rel="alternate" type="text/html" title="Migrate OSDs from one storage class to another" /><published>2022-11-28T20:00:00+00:00</published><updated>2022-11-28T20:00:00+00:00</updated><id>/openshift/2022/11/28/Migrate-ODF-osd-storageclass</id><content type="html" xml:base="/openshift/2022/11/28/Migrate-ODF-osd-storageclass.html"><![CDATA[<p>In this article, I’ll explain how to migrate your <strong>OpenShift Data Foundation</strong> OSDs (disks), residing on one cloud storage class, for example Azure managed-premium, to another storage class, for example Azure <strong>managed-csi</strong>, this will be a “rolling” migration, with no service and data interruption.</p>

<p><strong>Warning:</strong> If you are a Red Hat customer, open a support case before going forward, otherwise doing the following steps at your own risk!</p>

<h2 id="requirements">Requirements</h2>
<p>Before starting you’ll need:</p>

<ul>
  <li>installed and working OpenShift Data Foundation;</li>
  <li>configured new / destination Storage Class =&gt; in this article I’ll use <strong>managed-csi</strong>;</li>
  <li>ODF configured with <strong>replica</strong> parameter set to 3[0];</li>
  <li>in this guide I’ll move data / disks, only from 3 OSD disks to other 3 OSD disks, in different storage classes, if you have more than 3 OSD you have to redo this procedure from start to finish.</li>
</ul>

<p>[0] In this guide I’ll assume your OpenShift Data Foundation is installed on the hyperscaler cloud provider, for example Azure or AWS, with 3 availability zones, and with this configuration you should have replica set to 3:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc get storagecluster <span class="nt">-n</span> openshift-storage ocs-storagecluster <span class="nt">-ojson</span> | jq .spec.storageDeviceSets
<span class="o">[</span>
  <span class="o">{</span>
    <span class="s2">"count"</span>: 1,
...
    <span class="s2">"replica"</span>: 3,
...
  <span class="o">}</span>
<span class="o">]</span>
</code></pre></div></div>

<h2 id="run-must-gather">Run must-gather</h2>
<p>Before applying any change run an OpenShift must-gather:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc adm must-gather
</code></pre></div></div>

<p>then, create a specific ODF must-gather, in this example I use ODF in version 4.10:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">mkdir</span> ~/odf-must-gather
<span class="nv">$ </span>oc adm must-gather <span class="nt">--image</span><span class="o">=</span>registry.redhat.io/odf4/ocs-must-gather-rhel8:v4.10 <span class="nt">--dest-dir</span><span class="o">=</span>~/odf-must-gather
</code></pre></div></div>

<h2 id="check-cluster-health">Check cluster health</h2>
<p>Check if your cluster is healthy:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ NAMESPACE</span><span class="o">=</span>openshift-storage<span class="p">;</span><span class="nv">ROOK_POD</span><span class="o">=</span><span class="si">$(</span>oc <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> get pod <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>rook-ceph-operator <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[0].metadata.name}'</span><span class="si">)</span><span class="p">;</span>oc <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">ROOK_POD</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--</span> ceph status <span class="nt">--cluster</span><span class="o">=</span><span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--conf</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>.config <span class="nt">--keyring</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/client.admin.keyring | <span class="nb">grep </span>HEALTH
    health: HEALTH_OK
</code></pre></div></div>

<p><strong>WARNING:</strong> if your cluster is not in <strong>HEALTH_OK</strong>, stop any activities and check first ODF state!</p>

<h2 id="add-capacity">Add Capacity</h2>
<p>Add new capacity to your cluster using new StorageClass, in my case managed-csi navigating to:</p>

<ul>
  <li>select Left menu Operators =&gt; Installed Operators =&gt; OpenShift Data Foundation (selecting project openshift-storage on left corner);</li>
  <li>select “Storage System” tab;</li>
  <li>click on the three dot on the right and then select Add Capacity</li>
</ul>

<p><img src="/images/openshift/odf-move/01-add-capacity.png" alt="01-add-capacity.png" /></p>

<ul>
  <li>select your desired storage class and then click Add</li>
</ul>

<p><img src="/images/openshift/odf-move/02-add-capacity.png" alt="02-add-capacity.png" /></p>

<p>wait until ODF will rebalance all data, which means cluster will be in <strong>HEALTH_OK</strong> status and all placement groups (<strong>pgs</strong>) must be in <strong>active+clean</strong> state, to monitor rebalance you can use a while true infinite loop:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do </span><span class="nv">NAMESPACE</span><span class="o">=</span>openshift-storage<span class="p">;</span><span class="nv">ROOK_POD</span><span class="o">=</span><span class="si">$(</span>oc <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> get pod <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>rook-ceph-operator <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[0].metadata.name}'</span><span class="si">)</span><span class="p">;</span>oc <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">ROOK_POD</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--</span> ceph status <span class="nt">--cluster</span><span class="o">=</span><span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--conf</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>.config <span class="nt">--keyring</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/client.admin.keyring | egrep <span class="s1">'HEALTH_OK|HEALTH_WARN|[0-9]+\s+remapped|[0-9]+\/[0-9]+[ a-z]+misplaced[ ().%a-z0-9]+|'</span> <span class="p">;</span> <span class="nb">sleep </span>10 <span class="p">;</span> <span class="k">done
  </span>cluster:
    <span class="nb">id</span>:  ....
    health: HEALTH_OK

  services:
    mon: 3 daemons, quorum a,b,c <span class="o">(</span>age 4d<span class="o">)</span>
    mgr: a<span class="o">(</span>active, since 4d<span class="o">)</span>
    mds: 1/1 daemons up, 1 hot standby
    osd: 6 osds: 6 up <span class="o">(</span>since 6m<span class="o">)</span>, 6 <span class="k">in</span> <span class="o">(</span>since 6m<span class="o">)</span><span class="p">;</span> 135 remapped pgs
 
  data:
    volumes: 1/1 healthy
    pools:   4 pools, 193 pgs
    objects: 13.34k objects, 49 GiB
    usage:   147 GiB used, 2.8 TiB / 2.9 TiB avail
    pgs:     17928/40008 objects misplaced <span class="o">(</span>44.811%<span class="o">)</span>
             134 active+remapped+backfill_wait
             58  active+clean
             1   active+remapped+backfilling
 
  io:
    client:   4.8 KiB/s rd, 328 KiB/s wr, 2 op/s rd, 5 op/s wr
    recovery: 13 MiB/s, 3 objects/s
 
  progress:
    Global Recovery Event <span class="o">(</span>6m<span class="o">)</span>
      <span class="o">[========</span>....................] <span class="o">(</span>remaining: 14m<span class="o">)</span>
</code></pre></div></div>

<p>in the above example, you can see that ceph is rebalancing / remapping PGs, wait until all PGs are in <strong>active+clean</strong> state:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ NAMESPACE</span><span class="o">=</span>openshift-storage<span class="p">;</span><span class="nv">ROOK_POD</span><span class="o">=</span><span class="si">$(</span>oc <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> get pod <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>rook-ceph-operator <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[0].metadata.name}'</span><span class="si">)</span><span class="p">;</span>oc <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">ROOK_POD</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--</span> ceph status <span class="nt">--cluster</span><span class="o">=</span><span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--conf</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>.config <span class="nt">--keyring</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/client.admin.keyring
  cluster:
    <span class="nb">id</span>:     ....
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum a,b,c <span class="o">(</span>age 2d<span class="o">)</span>
    mgr: a<span class="o">(</span>active, since 2d<span class="o">)</span>
    mds: 1/1 daemons up, 1 hot standby
    osd: 6 osds: 6 up <span class="o">(</span>since 105m<span class="o">)</span>, 6 <span class="k">in</span> <span class="o">(</span>since 106m<span class="o">)</span>
 
  data:
    volumes: 1/1 healthy
    pools:   4 pools, 193 pgs
    objects: 45.33k objects, 73 GiB
    usage:   223 GiB used, 2.7 TiB / 2.9 TiB avail
    pgs:     193 active+clean
 
  io:
    client:   82 KiB/s rd, 12 MiB/s wr, 3 op/s rd, 113 op/s wr
</code></pre></div></div>

<p><strong>WARNING:</strong> wait until your cluster returns all PGs in <strong>active+clean</strong> state!</p>

<p>check also CephFS status:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ NAMESPACE</span><span class="o">=</span>openshift-storage<span class="p">;</span><span class="nv">ROOK_POD</span><span class="o">=</span><span class="si">$(</span>oc <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> get pod <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>rook-ceph-operator <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[0].metadata.name}'</span><span class="si">)</span><span class="p">;</span>oc <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">ROOK_POD</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--</span> ceph fs status <span class="nt">--cluster</span><span class="o">=</span><span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--conf</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>.config <span class="nt">--keyring</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/client.admin.keyring
ocs-storagecluster-cephfilesystem - 12 clients
<span class="o">=================================</span>
RANK      STATE                       MDS                     ACTIVITY     DNS    INOS   DIRS   CAPS  
 0        active      ocs-storagecluster-cephfilesystem-b  Reqs:   37 /s  34.8k  27.2k  8369   27.1k  
0-s   standby-replay  ocs-storagecluster-cephfilesystem-a  Evts:   47 /s  82.3k  26.8k  8298      0   
                   POOL                       TYPE     USED  AVAIL  
ocs-storagecluster-cephfilesystem-metadata  metadata   758M   697G  
 ocs-storagecluster-cephfilesystem-data0      data     212G   697G  
MDS version: ceph version 16.2.7-126.el8cp <span class="o">(</span>fe0af61d104d48cb9d116cde6e593b5fc8c197e4<span class="o">)</span> pacific <span class="o">(</span>stable<span class="o">)</span>
</code></pre></div></div>

<p><strong>WARNING</strong>: one of the two MDS must be in active state!</p>

<h2 id="identify-old-osds--disks-to-remove">Identify old OSDs / disks to remove</h2>
<p>Take a note of your 3 OSD id to remove, they are based on your old StorageClass, to see ODF OSD topology run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ NAMESPACE</span><span class="o">=</span>openshift-storage<span class="p">;</span><span class="nv">ROOK_POD</span><span class="o">=</span><span class="si">$(</span>oc <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> get pod <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>rook-ceph-operator <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[0].metadata.name}'</span><span class="si">)</span><span class="p">;</span>oc <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">ROOK_POD</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--</span> ceph osd tree <span class="nt">--cluster</span><span class="o">=</span><span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--conf</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>.config <span class="nt">--keyring</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/client.admin.keyring
ID   CLASS  WEIGHT   TYPE NAME                                                      STATUS  REWEIGHT  PRI-AFF
 <span class="nt">-1</span>         2.92978  root default                                                                            
 <span class="nt">-5</span>         2.92978      region westeurope                                                                   
<span class="nt">-14</span>         0.97659          zone westeurope-1                                                               
<span class="nt">-13</span>         0.48830              host clustername-ocs-westeurope1-qpdqh                               
  2    hdd  0.48830                  osd.2                                              up   1.00000  1.00000
<span class="nt">-17</span>         0.48830              host ocs-deviceset-managed-csi-1-data-0tfdrp                           
  3    hdd  0.48830                  osd.3                                              up   1.00000  1.00000
<span class="nt">-10</span>         0.97659          zone westeurope-2                                                               
 <span class="nt">-9</span>         0.48830              host clustername-ocs-westeurope2-46789                               
  1    hdd  0.48830                  osd.1                                              up   1.00000  1.00000
<span class="nt">-19</span>         0.48830              host ocs-deviceset-managed-csi-0-data-0zzxzr                           
  4    hdd  0.48830                  osd.4                                              up   1.00000  1.00000
 <span class="nt">-4</span>         0.97659          zone westeurope-3                                                               
 <span class="nt">-3</span>         0.48830              host clustername-ocs-westeurope3-9wsjs                               
  0    hdd  0.48830                  osd.0                                              up   1.00000  1.00000
<span class="nt">-21</span>         0.48830              host ocs-deviceset-managed-csi-2-data-0bc889                           
  5    hdd  0.48830                  osd.5                                              up   1.00000  1.00000
</code></pre></div></div>

<p>In my case the old OSD are osd.0, osd.1 and osd.2, those OSDs needs to be removed / deleted one by one, waiting for <strong>HEALTH_OK</strong> after every removing / deleting.</p>

<h2 id="remove-osd-in-old-storageclass">Remove OSD in old StorageClass</h2>
<h3 id="switch-to-openshift-storage-project">Switch to openshift-storage project</h3>
<p>First switch to openshift-storage project:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc project openshift-storage
</code></pre></div></div>

<h3 id="copy-ceph-config-and-keyring-files">Copy Ceph config and keyring files</h3>
<p>Copy your Ceph config file and keyring file from rook container pod to your Linux box, then those files will be transferred to one mon container in order to run ceph commands after scaling down rook operator.</p>

<p>Copy files from rook container to your Linux box:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ ROOK</span><span class="o">=</span><span class="si">$(</span>oc get pod | <span class="nb">grep </span>rook-ceph-operator | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>

<span class="nv">$ </span>oc rsync <span class="k">${</span><span class="nv">ROOK</span><span class="k">}</span>:/var/lib/rook/openshift-storage/openshift-storage.config <span class="nb">.</span>
WARNING: cannot use rsync: rsync not available <span class="k">in </span>container
openshift-storage.config
<span class="nv">$ </span>oc rsync <span class="k">${</span><span class="nv">ROOK</span><span class="k">}</span>:/var/lib/rook/openshift-storage/client.admin.keyring <span class="nb">.</span>
WARNING: cannot use rsync: rsync not available <span class="k">in </span>container
client.admin.keyring
</code></pre></div></div>

<p>Copy openshift-storage.config and openshift-storage.config files from your Linux box to one mon container:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ MONA</span><span class="o">=</span><span class="si">$(</span>oc get pod | <span class="nb">grep </span>rook-ceph-mon | egrep <span class="s1">'2\/2\s+Running'</span> | <span class="nb">head</span> <span class="nt">-n1</span> | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>

<span class="nv">$ </span>oc <span class="nb">cp </span>openshift-storage.config <span class="k">${</span><span class="nv">MONA</span><span class="k">}</span>:/tmp/openshift-storage.config
Defaulted container <span class="s2">"mon"</span> out of: mon, log-collector, chown-container-data-dir <span class="o">(</span>init<span class="o">)</span>, init-mon-fs <span class="o">(</span>init<span class="o">)</span>
<span class="nv">$ </span>oc <span class="nb">cp </span>client.admin.keyring <span class="k">${</span><span class="nv">MONA</span><span class="k">}</span>:/tmp/client.admin.keyring
Defaulted container <span class="s2">"mon"</span> out of: mon, log-collector, chown-container-data-dir <span class="o">(</span>init<span class="o">)</span>, init-mon-fs <span class="o">(</span>init<span class="o">)</span>
</code></pre></div></div>

<p><strong>NOTE</strong>: MONA, in one of Italian regional language means stupid people :smile:</p>

<p>Check ceph command on MONA container:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc rsh <span class="k">${</span><span class="nv">MONA</span><span class="k">}</span>
Defaulted container <span class="s2">"mon"</span> out of: mon, log-collector, chown-container-data-dir <span class="o">(</span>init<span class="o">)</span>, init-mon-fs <span class="o">(</span>init<span class="o">)</span>

sh-4.4# ceph health <span class="nt">--cluster</span><span class="o">=</span>openshift-storage <span class="nt">--conf</span><span class="o">=</span>/tmp/openshift-storage.config <span class="nt">--keyring</span><span class="o">=</span>/tmp/client.admin.keyring
2022-XX <span class="nt">-1</span> auth: unable to find a keyring on /var/lib/rook/openshift-storage/client.admin.keyring: <span class="o">(</span>2<span class="o">)</span> No such file or directory
2022-XX <span class="nt">-1</span> AuthRegistry<span class="o">(</span>0x7fa63805bb68<span class="o">)</span> no keyring found at /var/lib/rook/openshift-storage/client.admin.keyring, disabling cephx
HEALTH_OK
sh-4.4#
</code></pre></div></div>

<h3 id="scale-down-openshift-data-foundation-operators">Scale down OpenShift Data Foundation operators</h3>
<p>Now we can scale to zero rook and ocs operators:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc scale deploy ocs-operator <span class="nt">--replicas</span><span class="o">=</span>0
deployment.apps/ocs-operator scaled
<span class="nv">$ </span>oc scale deploy rook-ceph-operator <span class="nt">--replicas</span><span class="o">=</span>0
deployment.apps/rook-ceph-operator scaled
</code></pre></div></div>

<h3 id="remove-one-osd">Remove one OSD</h3>
<p>Now you can remove one OSD, in my case I’ll remove osd.0 (zero), but in your case could be a different ID.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ failed_osd_id</span><span class="o">=</span>0
<span class="nv">$ </span><span class="nb">export </span><span class="nv">PS1</span><span class="o">=</span><span class="s2">"[</span><span class="se">\u</span><span class="s2">@</span><span class="se">\h</span><span class="s2"> </span><span class="se">\W</span><span class="s2">]</span><span class="se">\ </span><span class="s2">OSD=</span><span class="nv">$failed_osd_id</span><span class="s2"> </span><span class="nv">$ </span><span class="s2">"</span>

<span class="nv">$ </span>oc scale deploy rook-ceph-osd-<span class="k">${</span><span class="nv">failed_osd_id</span><span class="k">}</span> <span class="nt">--replicas</span><span class="o">=</span>0
deployment.apps/rook-ceph-osd-0 scaled

<span class="nv">$ </span>oc process <span class="nt">-n</span> openshift-storage ocs-osd-removal  <span class="nt">-p</span> <span class="nv">FAILED_OSD_IDS</span><span class="o">=</span><span class="k">${</span><span class="nv">failed_osd_id</span><span class="k">}</span> | oc create <span class="nt">-f</span> -
job.batch/ocs-osd-removal-job created

<span class="nv">$ JOBREMOVAL</span><span class="o">=</span><span class="si">$(</span>oc get pod | <span class="nb">grep </span>ocs-osd-removal-job- | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>

<span class="nv">$ </span>oc logs <span class="k">${</span><span class="nv">JOBREMOVAL</span><span class="k">}</span> | egrep <span class="s2">"cephosd: completed removal of OSD </span><span class="k">${</span><span class="nv">failed_osd_id</span><span class="k">}</span><span class="s2">"</span>
2022-XX I | cephosd: completed removal of OSD 0
</code></pre></div></div>

<p><strong>NOTE</strong>: on the last command you must see <strong>cephosd: completed removal of OSD X</strong>, where X is your osd id (in my case zero).</p>

<p>check ceph health status, where you can see a degraded state due to one osd removal:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc rsh <span class="k">${</span><span class="nv">MONA</span><span class="k">}</span>
Defaulted container <span class="s2">"mon"</span> out of: mon, log-collector, chown-container-data-dir <span class="o">(</span>init<span class="o">)</span>, init-mon-fs <span class="o">(</span>init<span class="o">)</span>
sh-4.4# ceph status <span class="nt">--cluster</span><span class="o">=</span>openshift-storage <span class="nt">--conf</span><span class="o">=</span>/tmp/openshift-storage.config <span class="nt">--keyring</span><span class="o">=</span>/tmp/client.admin.keyring
  cluster:
    <span class="nb">id</span>:     ....
    health: HEALTH_WARN
            Degraded data redundancy: 19562/138537 objects degraded <span class="o">(</span>14.120%<span class="o">)</span>, 96 pgs degraded, 96 pgs undersized
 
  services:
    mon: 3 daemons, quorum a,b,c <span class="o">(</span>age 2d<span class="o">)</span>
    mgr: a<span class="o">(</span>active, since 2d<span class="o">)</span>
    mds: 1/1 daemons up, 1 hot standby
    osd: 5 osds: 5 up <span class="o">(</span>since 6m<span class="o">)</span>, 5 <span class="k">in</span> <span class="o">(</span>since 3m<span class="o">)</span><span class="p">;</span> 110 remapped pgs
 
  data:
    volumes: 1/1 healthy
    pools:   4 pools, 193 pgs
    objects: 46.18k objects, 73 GiB
    usage:   192 GiB used, 2.3 TiB / 2.4 TiB avail
    pgs:     19562/138537 objects degraded <span class="o">(</span>14.120%<span class="o">)</span>
             6098/138537 objects misplaced <span class="o">(</span>4.402%<span class="o">)</span>
             95 active+undersized+degraded+remapped+backfill_wait
             83 active+clean
             14 active+remapped+backfill_wait
             1  active+undersized+degraded+remapped+backfilling
 
  io:
    client:   131 KiB/s rd, 14 MiB/s wr, 4 op/s rd, 151 op/s wr
    recovery: 1023 KiB/s, 14 keys/s, 7 objects/s
 
sh-4.4#
</code></pre></div></div>

<p>wait until ceph returns HEALTH_OK and all PGs are <strong>active+clean</strong>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sh-4.4# <span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do </span>ceph status <span class="nt">--cluster</span><span class="o">=</span>openshift-storage <span class="nt">--conf</span><span class="o">=</span>/tmp/openshift-storage.config <span class="nt">--keyring</span><span class="o">=</span>/tmp/client.admin.keyring | egrep <span class="nt">--color</span><span class="o">=</span>always <span class="s1">'[0-9]+\/[0-9]+.*(degraded|misplaced)|'</span> <span class="p">;</span> <span class="nb">sleep </span>10 <span class="p">;</span> <span class="k">done
  </span>cluster:
    <span class="nb">id</span>:     ....
    health: HEALTH_WARN
            Degraded data redundancy: 17957/136521 objects degraded <span class="o">(</span>13.153%<span class="o">)</span>, 91 pgs degraded, 91 pgs undersized
 
  services:
    mon: 3 daemons, quorum a,b,c <span class="o">(</span>age 2d<span class="o">)</span>
    mgr: a<span class="o">(</span>active, since 2d<span class="o">)</span>
    mds: 1/1 daemons up, 1 hot standby
    osd: 5 osds: 5 up <span class="o">(</span>since 8m<span class="o">)</span>, 5 <span class="k">in</span> <span class="o">(</span>since 6m<span class="o">)</span><span class="p">;</span> 105 remapped pgs
 
  data:
    volumes: 1/1 healthy
    pools:   4 pools, 193 pgs
    objects: 45.51k objects, 73 GiB
    usage:   194 GiB used, 2.3 TiB / 2.4 TiB avail
    pgs:     17957/136521 objects degraded <span class="o">(</span>13.153%<span class="o">)</span>
             5767/136521 objects misplaced <span class="o">(</span>4.224%<span class="o">)</span>
             90 active+undersized+degraded+remapped+backfill_wait
             88 active+clean
             14 active+remapped+backfill_wait
             1  active+undersized+degraded+remapped+backfilling
 
  io:
    client:   90 KiB/s rd, 14 MiB/s wr, 2 op/s rd, 145 op/s wr
    recovery: 1023 KiB/s, 20 keys/s, 10 objects/s


...... <span class="o">(</span><span class="nb">cut</span><span class="o">)</span>

  cluster:
    <span class="nb">id</span>:     ....
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum a,b,c <span class="o">(</span>age 2d<span class="o">)</span>
    mgr: a<span class="o">(</span>active, since 2d<span class="o">)</span>
    mds: 1/1 daemons up, 1 hot standby
    osd: 5 osds: 5 up <span class="o">(</span>since 61m<span class="o">)</span>, 5 <span class="k">in</span> <span class="o">(</span>since 58m<span class="o">)</span>
 
  data:
    volumes: 1/1 healthy
    pools:   4 pools, 193 pgs
    objects: 45.63k objects, 74 GiB
    usage:   226 GiB used, 2.2 TiB / 2.4 TiB avail
    pgs:     193 active+clean
 
  io:
    client:   115 KiB/s rd, 14 MiB/s wr, 4 op/s rd, 139 op/s wr

</code></pre></div></div>

<p><strong>WARNING</strong>: before going forward you must wait for ceph <strong>HEALTH_OK</strong> and all PGs in <strong>active+clean</strong> state!</p>

<p>Delete removal job:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc delete job ocs-osd-removal-job
job.batch <span class="s2">"ocs-osd-removal-job"</span> deleted
</code></pre></div></div>

<p>Repeat these steps for each OSD you need to remove (in my case for osd.1 and osd.2)</p>

<h2 id="remove-your-old-storage-class">Remove your old storage class</h2>
<p>After removing all OSD that belongs to your old storageclass (in my case Azure managed-premium), you can edit your <strong>storagecluster</strong> object to remove any pointer to the old storage class.</p>

<p>Make a backup before editing your storagecluster:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc get storagecluster ocs-storagecluster <span class="nt">-oyaml</span> | <span class="nb">tee </span>storagecluster-ocs-storagecluster-before-remove-managed-premium.yaml
</code></pre></div></div>

<p>change / edit your storagecluster storageDeviceSets, from having OSD from both “old” storageclass (in my case managed-premium) and “new” storageclass (in my case managed-csi):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc get storagecluster ocs-storagecluster <span class="nt">-oyaml</span>
....
  storageDeviceSets:
  - config: <span class="o">{}</span>
    count: 1
    dataPVCTemplate:
      metadata: <span class="o">{}</span>
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 500Gi
        storageClassName: managed-premium
        volumeMode: Block
      status: <span class="o">{}</span>
    name: ocs-deviceset
    placement: <span class="o">{}</span>
    preparePlacement: <span class="o">{}</span>
    replica: 3
    resources:
      limits:
        cpu: <span class="s2">"2"</span>
        memory: 5Gi
      requests:
        cpu: <span class="s2">"2"</span>
        memory: 5Gi
  - count: 1
    dataPVCTemplate:
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 500Gi
        storageClassName: managed-csi
        volumeMode: Block
    name: ocs-deviceset-managed-csi
    placement: <span class="o">{}</span>
    portable: <span class="nb">true
    </span>replica: 3
    resources: <span class="o">{}</span>
</code></pre></div></div>

<p>to have only “new” storageclass (in my case managed-csi):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc edit storagecluster ocs-storagecluster
....        
  storageDeviceSets:
  - count: 1
    dataPVCTemplate:
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 500Gi
        storageClassName: managed-csi
        volumeMode: Block
    name: ocs-deviceset-managed-csi
    placement: <span class="o">{}</span>
    portable: <span class="nb">true
    </span>replica: 3
    resources:
      limits:
        cpu: <span class="s2">"2"</span>
        memory: 5Gi
      requests:
        cpu: <span class="s2">"2"</span>
        memory: 5Gi
</code></pre></div></div>

<h2 id="scale-up-openshift-data-foundation-operators">Scale up OpenShift Data Foundation operators</h2>
<p>At this point you can scale up ocs-operator:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>oc scale deploy ocs-operator <span class="nt">--replicas</span><span class="o">=</span>1
deployment.apps/ocs-operator scaled
</code></pre></div></div>

<p>and then re-check Ceph health status:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ NAMESPACE</span><span class="o">=</span>openshift-storage<span class="p">;</span><span class="nv">ROOK_POD</span><span class="o">=</span><span class="si">$(</span>oc <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> get pod <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>rook-ceph-operator <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[0].metadata.name}'</span><span class="si">)</span><span class="p">;</span>oc <span class="nb">exec</span> <span class="nt">-it</span> <span class="k">${</span><span class="nv">ROOK_POD</span><span class="k">}</span> <span class="nt">-n</span> <span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--</span> ceph status <span class="nt">--cluster</span><span class="o">=</span><span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span> <span class="nt">--conf</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>.config <span class="nt">--keyring</span><span class="o">=</span>/var/lib/rook/<span class="k">${</span><span class="nv">NAMESPACE</span><span class="k">}</span>/client.admin.keyring | egrep <span class="nt">-i</span> <span class="s1">'remapped|misplaced|active\+clean|HEALTH_OK|'</span>
  cluster:
    <span class="nb">id</span>:     ....
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum a,b,c <span class="o">(</span>age 3d<span class="o">)</span>
    mgr: a<span class="o">(</span>active, since 3d<span class="o">)</span>
    mds: 1/1 daemons up, 1 hot standby
    osd: 3 osds: 3 up <span class="o">(</span>since 2h<span class="o">)</span>, 3 <span class="k">in</span> <span class="o">(</span>since 2h<span class="o">)</span>
 
  data:
    volumes: 1/1 healthy
    pools:   4 pools, 193 pgs
    objects: 45.87k objects, 74 GiB
    usage:   226 GiB used, 1.2 TiB / 1.5 TiB avail
    pgs:     193 active+clean
 
  io:
    client:   134 KiB/s rd, 60 MiB/s wr, 4 op/s rd, 322 op/s wr
</code></pre></div></div>]]></content><author><name></name></author><category term="OpenShift" /><category term="OpenShift" /><category term="odf" /><summary type="html"><![CDATA[In this article, I’ll explain how to migrate your OpenShift Data Foundation OSDs (disks), residing on one cloud storage class, for example Azure managed-premium, to another storage class, for example Azure managed-csi, this will be a “rolling” migration, with no service and data interruption.]]></summary></entry><entry><title type="html">Install OpenShift IPI for homelab on Hetzner Root servers</title><link href="/openshift/2022/10/02/Install-OpenShift-IPI-lab-on-Hetzner.html" rel="alternate" type="text/html" title="Install OpenShift IPI for homelab on Hetzner Root servers" /><published>2022-10-02T08:00:00+00:00</published><updated>2022-10-02T08:00:00+00:00</updated><id>/openshift/2022/10/02/Install-OpenShift-IPI-lab-on-Hetzner</id><content type="html" xml:base="/openshift/2022/10/02/Install-OpenShift-IPI-lab-on-Hetzner.html"><![CDATA[<p><strong>Warning:</strong> This document / project / repository / playbooks should be used <strong>only for testing</strong> OpenShift Container Platform 4.x and <strong>NOT for production environments</strong>.</p>

<p>In this article, I’ll explain how to deploy Red Hat OpenShift Container Platform using the Hetzner Root Server(s), this guide is similar to my previously <a href="https://amedeos.github.io/openshift/2022/08/20/Install-OpenShift-IPI-bm-on-lab.html">Install OpenShift baremetal IPI on homelab, using nested virtualization</a>, but on this case the KVM hosts will be Hetzner Root Servers, upon them (or it) you will have OpenShift baremetal IPI installed using nested virtualization.</p>

<h2 id="install-centos-stream-8">Install CentOS Stream 8</h2>
<p>On all your Hetzner Root Server install CentOS Stream 8 operating system.</p>

<p>Before going further I’d suggest to upload your SSH public key to Hetzner robot navigating to Server =&gt; click on <strong>Key management</strong> and then you can click to <strong>New key</strong>; in the new window set a name and paste your public key content in <strong>Key data</strong>;</p>

<ul>
  <li>activate Rescue system by logging in to your robot account, then navigate to <strong>Server</strong> tab =&gt; select your Root Server =&gt; select the <strong>Rescue</strong> sub tab =&gt; click to <strong>Activate rescue system</strong> button:</li>
</ul>

<p><img src="/images/openshift/hetzner/01-activate-rescue.png" alt="01-activate-rescue.png" /></p>

<ul>
  <li>Reset your server in order to reboot in rescue mode; for this you can simply run <code class="language-plaintext highlighter-rouge">reboot</code> on SSH, or you can send a reset using Hetzner robot by selecting <strong>Reset</strong> sub menu =&gt; set Reset type to Execute an automatic hardware reset =&gt; click on the <strong>Send</strong> button:</li>
</ul>

<p><img src="/images/openshift/hetzner/02-send-reset.png" alt="02-send-reset.png" /></p>

<ul>
  <li>
    <p><strong>wait</strong> until your root server is rebooted in rescue mode;</p>
  </li>
  <li>
    <p>connect in SSH to your Root server and wipe all your disks:</p>
  </li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@rescue ~ <span class="c"># dd if=/dev/zero of=/dev/nvme0n1 bs=1M count=10000 oflag=direct status=progress</span>
root@rescue ~ <span class="c"># dd if=/dev/zero of=/dev/nvme1n1 bs=1M count=10000 oflag=direct status=progress</span>

root@rescue ~ <span class="c"># dd if=/dev/zero of=/dev/sda bs=1M count=10000 oflag=direct status=progress</span>
root@rescue ~ <span class="c"># dd if=/dev/zero of=/dev/sdb bs=1M count=10000 oflag=direct status=progress</span>
</code></pre></div></div>

<ul>
  <li>run <code class="language-plaintext highlighter-rouge">installimage</code>:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@rescue ~ <span class="c"># installimage</span>
</code></pre></div></div>

<ul>
  <li>select CentOS Stream 8:</li>
</ul>

<p><img src="/images/openshift/hetzner/03-centos-01.png" alt="03-centos-01.png" /></p>

<p><img src="/images/openshift/hetzner/03-centos-02.png" alt="03-centos-02.png" /></p>

<ul>
  <li>press enter:</li>
</ul>

<p><img src="/images/openshift/hetzner/03-centos-03.png" alt="03-centos-03.png" /></p>

<ul>
  <li>disable software RAID:</li>
</ul>

<p><strong>REMEMBER: this guide is for automating you OpenShift Lab and NOT for production environment(s)</strong></p>

<p><img src="/images/openshift/hetzner/03-centos-04.png" alt="03-centos-04.png" /></p>

<ul>
  <li>create two partitions, one for <strong>boot</strong> with 2G dimension and second one with <strong>lvm</strong> using all remaining space; then create four logical volume, but remember to use <strong>xfs</strong> for file system and to give at least 20G for /tmp file system (sushy-emulator will use /tmp to build iso files); finally comment default Hetzner three partitions:</li>
</ul>

<p><img src="/images/openshift/hetzner/03-centos-05.png" alt="03-centos-05.png" /></p>

<ul>
  <li>press ESC and then with arrow keys select Save before close:</li>
</ul>

<p><img src="/images/openshift/hetzner/03-centos-06.png" alt="03-centos-06.png" /></p>

<ul>
  <li>press enter to confirm:</li>
</ul>

<p><img src="/images/openshift/hetzner/03-centos-07.png" alt="03-centos-07.png" /></p>

<p><img src="/images/openshift/hetzner/03-centos-08.png" alt="03-centos-08.png" /></p>

<ul>
  <li>wait until the installation is completed and then <code class="language-plaintext highlighter-rouge">reboot</code>:</li>
</ul>

<p><img src="/images/openshift/hetzner/03-centos-09.png" alt="03-centos-09.png" /></p>

<ul>
  <li>repeat above installation steps for all your Hetzner Root Servers.</li>
</ul>

<h2 id="create-baremetal-vlan">Create baremetal VLAN</h2>
<p>On Hetzner Root Server you can use vSwitch to connect multiple servers to the same VLAN.</p>

<ul>
  <li>select Server =&gt; vSwitches:</li>
</ul>

<p><img src="/images/openshift/hetzner/04-vswitch-01.png" alt="04-vswitch-01.png" /></p>

<ul>
  <li>create a <strong>baremetal</strong> vSwitch with <strong>4000</strong> VLAN ID, then click on <strong>Create vSwitch</strong>:</li>
</ul>

<p><img src="/images/openshift/hetzner/04-vswitch-02.png" alt="04-vswitch-02.png" /></p>

<ul>
  <li>add your servers to baremetal vSwitch:</li>
</ul>

<p><img src="/images/openshift/hetzner/04-vswitch-03.png" alt="04-vswitch-03.png" /></p>

<p><img src="/images/openshift/hetzner/04-vswitch-04.png" alt="04-vswitch-04.png" /></p>

<ul>
  <li>wait until all servers are added to baremetal vSwitch:</li>
</ul>

<p><img src="/images/openshift/hetzner/04-vswitch-05.png" alt="04-vswitch-05.png" /></p>

<h2 id="create-your-custom-variables">Create your custom variables</h2>
<p>These steps must not be run directly on your Root Server(s) because them will be rebooted in order to configure properly.</p>

<ul>
  <li>clone <a href="https://github.com/amedeos/ocp4-in-the-jars">ocp4-in-the-jars</a> project in your box:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone https://github.com/amedeos/ocp4-in-the-jars
</code></pre></div></div>

<ul>
  <li>cd into ocp4-in-the-jars directory:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd </span>ocp4-in-the-jars
</code></pre></div></div>

<ul>
  <li>create your <strong>hosts-kvmhost</strong> file, where most important you need to choose one single baremetal_ip for each host; an example could be:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat </span>hosts-kvmhost 
<span class="o">[</span>kvmhost]
hetlab01 <span class="nv">baremetal_ip</span><span class="o">=</span>192.168.203.3 <span class="nv">ansible_ssh_user</span><span class="o">=</span>root <span class="nv">ansible_ssh_common_args</span><span class="o">=</span><span class="s1">'-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'</span>
hetlab02 <span class="nv">baremetal_ip</span><span class="o">=</span>192.168.203.4 <span class="nv">ansible_ssh_user</span><span class="o">=</span>root <span class="nv">ansible_ssh_common_args</span><span class="o">=</span><span class="s1">'-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'</span>
</code></pre></div></div>

<p>in my case, I use an ssh config file to resolve hetlab01 and hetlab02, but you can set ansible_ssh_host variable to default IP of your root server:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat </span>hosts-kvmhost 
<span class="o">[</span>kvmhost]
hetlab01 <span class="nv">baremetal_ip</span><span class="o">=</span>192.168.203.3 <span class="nv">ansible_ssh_host</span><span class="o">=</span>x.x.x.x <span class="nv">ansible_ssh_user</span><span class="o">=</span>root <span class="nv">ansible_ssh_common_args</span><span class="o">=</span><span class="s1">'-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'</span>
hetlab02 <span class="nv">baremetal_ip</span><span class="o">=</span>192.168.203.4 <span class="nv">ansible_ssh_host</span><span class="o">=</span>y.y.y.y <span class="nv">ansible_ssh_user</span><span class="o">=</span>root <span class="nv">ansible_ssh_common_args</span><span class="o">=</span><span class="s1">'-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'</span>
</code></pre></div></div>

<ul>
  <li>test Ansible connection:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ansible <span class="nt">-m</span> ping all <span class="nt">-i</span> hosts-kvmhost <span class="nt">-o</span> <span class="nt">-b</span>
hetlab01 | SUCCESS <span class="o">=&gt;</span> <span class="o">{</span><span class="s2">"ansible_facts"</span>: <span class="o">{</span><span class="s2">"discovered_interpreter_python"</span>: <span class="s2">"/usr/libexec/platform-python"</span><span class="o">}</span>,<span class="s2">"changed"</span>: <span class="nb">false</span>,<span class="s2">"ping"</span>: <span class="s2">"pong"</span><span class="o">}</span>
hetlab02 | SUCCESS <span class="o">=&gt;</span> <span class="o">{</span><span class="s2">"ansible_facts"</span>: <span class="o">{</span><span class="s2">"discovered_interpreter_python"</span>: <span class="s2">"/usr/libexec/platform-python"</span><span class="o">}</span>,<span class="s2">"changed"</span>: <span class="nb">false</span>,<span class="s2">"ping"</span>: <span class="s2">"pong"</span><span class="o">}</span>
</code></pre></div></div>

<ul>
  <li>create <strong>custom-variables.yaml</strong> file:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">tee</span> <span class="s2">"custom-variables.yaml"</span> <span class="o">&gt;</span> /dev/null <span class="o">&lt;&lt;</span><span class="sh">'</span><span class="no">EOF</span><span class="sh">'
baremetal_net:
  net: 192.168.203.0
  netmask: 255.255.255.0
  prefix: 24
  reverse: 203.168.192
  gateway: 192.168.203.1
  ntp: "103.16.182.23,103.16.182.214"
  dhcp_start: 192.168.203.90
  dhcp_end: 192.168.203.110
  mtu: 1400
  vlan: 4000
kvmhost:
  enable_selinux: True
  reboot_timeout: 1200
  enable_portfw: True
  replace_ddns_duckdns: False
  provisioning_bridge_create: True
  provisioning_bridge_isolated: False
  baremetal_bridge_create: True
  baremetal_bridge_isolated: False
  enable_baremetal_gw: True
  set_hostname: True
  set_hosts: True
  additional_hosts: personal_hosts.j2
  create_ssh_key: True
secure_password: XXXXXXX
rh_subcription_user: XXXXXXX
rh_subcription_password: XXXXXXX
rh_subcription_pool: XXXXXXX
</span><span class="no">EOF
</span></code></pre></div></div>

<p>replace last four variables <strong>secure_password, rh_subcription_user, rh_subcription_password and rh_subcription_pool</strong> with your correct data, if you have any doubt have a look at <a href="https://amedeos.github.io/openshift/2022/08/20/Install-OpenShift-IPI-bm-on-lab.html">Install OpenShift baremetal IPI on homelab, using nested virtualization</a></p>

<ul>
  <li>create <strong>custom-bm-ansible-nodes.json</strong> file, where inside it, you can choose where every VMs will be created (across your Root Servers);</li>
</ul>

<p>for example master-0 will be created on hetlab01.example.com host, where redfish_ip match baremetal_ip into <strong>hosts-kvmhost</strong> file:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"master-0"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"state"</span><span class="p">:</span><span class="w"> </span><span class="s2">"present"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"hypervisor_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hetlab01.example.com"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"hypervisor_user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"root"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"hypervisor_ssh_key"</span><span class="p">:</span><span class="w"> </span><span class="s2">"~/.ssh/id_rsa"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"hypervisor_image_dir"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/var/lib/libvirt/images"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"provisioning_mac"</span><span class="p">:</span><span class="w"> </span><span class="s2">"52:54:00:00:32:00"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"baremetal_mac"</span><span class="p">:</span><span class="w"> </span><span class="s2">"52:54:00:00:33:00"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"vbmc_pre_cmd"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
            </span><span class="nl">"vbmc_ip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192.168.201.102"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"vbmc_port"</span><span class="p">:</span><span class="w"> </span><span class="s2">"623"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"redfish_ip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192.168.203.3"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"redfish_port"</span><span class="p">:</span><span class="w"> </span><span class="s2">"8000"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"baremetal_ip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192.168.203.53"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"baremetal_last"</span><span class="p">:</span><span class="w"> </span><span class="s2">"53"</span><span class="w">
        </span><span class="p">}</span><span class="err">,</span><span class="w">
</span></code></pre></div></div>

<p>instead worker-0 will be created on hetlab02.example.com host:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"worker-0"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"state"</span><span class="p">:</span><span class="w"> </span><span class="s2">"present"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"is_odf"</span><span class="p">:</span><span class="w"> </span><span class="s2">"true"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"hypervisor_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hetlab02.example.com"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"hypervisor_user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"root"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"hypervisor_ssh_key"</span><span class="p">:</span><span class="w"> </span><span class="s2">"~/.ssh/id_rsa"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"hypervisor_image_dir"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/var/lib/libvirt/images"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"provisioning_mac"</span><span class="p">:</span><span class="w"> </span><span class="s2">"52:54:00:00:32:03"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"baremetal_mac"</span><span class="p">:</span><span class="w"> </span><span class="s2">"52:54:00:00:33:03"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"vbmc_pre_cmd"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
            </span><span class="nl">"vbmc_ip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192.168.201.13"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"vbmc_port"</span><span class="p">:</span><span class="w"> </span><span class="s2">"623"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"redfish_ip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192.168.203.4"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"redfish_port"</span><span class="p">:</span><span class="w"> </span><span class="s2">"8000"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"baremetal_ip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192.168.203.56"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"baremetal_last"</span><span class="p">:</span><span class="w"> </span><span class="s2">"56"</span><span class="w">
        </span><span class="p">}</span><span class="err">,</span><span class="w">
</span></code></pre></div></div>

<p>have a look at <a href="https://github.com/amedeos/ocp4-in-the-jars/blob/main/custom-bm-ansible-nodes-hetzner-example.json">custom-bm-ansible-nodes.json example</a> file.</p>

<ul>
  <li>
    <p>download <strong>pull-secret.txt</strong> from <a href="https://console.redhat.com/">Red Hat Console</a> and place it as pull-secret.txt; if you have any doubt have a look at <a href="https://amedeos.github.io/openshift/2022/08/20/Install-OpenShift-IPI-bm-on-lab.html">Install OpenShift baremetal IPI on homelab, using nested virtualization</a></p>
  </li>
  <li>
    <p>download RHEL 8.6 qcow2 file from <a href="https://access.redhat.com/downloads/">Red Hat Downloads</a>; if you have any doubt have a look at <a href="https://amedeos.github.io/openshift/2022/08/20/Install-OpenShift-IPI-bm-on-lab.html">Install OpenShift baremetal IPI on homelab, using nested virtualization</a></p>
  </li>
</ul>

<h2 id="run-ansible-playbook-prepare-hypervisoryaml">Run Ansible playbook prepare-hypervisor.yaml</h2>
<p>Now you can run the ansible playbook prepare-hypervisor.yaml:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ansible-playbook <span class="nt">-i</span> hosts-kvmhost <span class="nt">--extra-vars</span> <span class="s2">"@custom-variables.yaml"</span> prepare-hypervisor.yaml
</code></pre></div></div>

<p>this playbook will configure your Root Server.</p>

<h2 id="copy-custom-variables">Copy Custom variables</h2>

<ul>
  <li>copy RHEL 8.6 qcow2 file to all your Root Servers:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>scp /tmp/rhel-8.6-x86_64-kvm.qcow2 hetlab01:/root/images/
<span class="nv">$ </span>scp /tmp/rhel-8.6-x86_64-kvm.qcow2 hetlab02:/root/images/
</code></pre></div></div>

<ul>
  <li>clone repository on one Root Server:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ssh hetlab01
<span class="o">[</span>root@hetlab01 ~]# git clone https://github.com/amedeos/ocp4-in-the-jars.git
</code></pre></div></div>

<ul>
  <li>copy <strong>custom-variables.yaml</strong> file:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>scp custom-variables.yaml hetlab01:/root/ocp4-in-the-jars/
</code></pre></div></div>

<ul>
  <li>copy <strong>custom-bm-ansible-nodes.json</strong> file:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>scp custom-bm-ansible-nodes.json hetlab01:/root/ocp4-in-the-jars/
</code></pre></div></div>

<ul>
  <li>copy <strong>pull-secret.txt</strong> file:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scp pull-secret.txt hetlab01:/root/ocp4-in-the-jars/
</code></pre></div></div>

<h2 id="run-the-installation">Run the installation</h2>
<p>Finally you can install OpenShift by running Ansible playbook main.yaml in a <strong>tmux</strong> session:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ssh hetlab01
<span class="o">[</span>root@hetlab01 ~]# tmux

<span class="o">[</span>root@hetlab01 ~]# <span class="nb">cd</span> /root/ocp4-in-the-jars
<span class="o">[</span>root@hetlab01 ocp4-in-the-jars]# ansible-playbook <span class="nt">--extra-vars</span> <span class="s2">"@custom-variables.yaml"</span> <span class="nt">--extra-vars</span> <span class="s2">"@custom-bm-ansible-nodes.json"</span> main.yaml
</code></pre></div></div>

<p>wait 1-3 hours until the installation completes.</p>]]></content><author><name></name></author><category term="OpenShift" /><category term="OpenShift" /><category term="ipi" /><summary type="html"><![CDATA[Warning: This document / project / repository / playbooks should be used only for testing OpenShift Container Platform 4.x and NOT for production environments.]]></summary></entry><entry><title type="html">Install OpenShift baremetal IPI on homelab, using nested virtualization</title><link href="/openshift/2022/08/20/Install-OpenShift-IPI-bm-on-lab.html" rel="alternate" type="text/html" title="Install OpenShift baremetal IPI on homelab, using nested virtualization" /><published>2022-08-20T08:00:00+00:00</published><updated>2022-08-20T08:00:00+00:00</updated><id>/openshift/2022/08/20/Install-OpenShift-IPI-bm-on-lab</id><content type="html" xml:base="/openshift/2022/08/20/Install-OpenShift-IPI-bm-on-lab.html"><![CDATA[<p><strong>Warning:</strong> This document / project / repository / playbooks should be used <strong>only for testing</strong> OpenShift Container Platform 4.x and <strong>NOT for production environments</strong>.</p>

<p>In this article, I’ll explain how to deploy Red Hat OpenShift Container Platform using the <a href="https://docs.openshift.com/container-platform/4.11/installing/installing_bare_metal_ipi/ipi-install-overview.html">installer-provisioned cluster on bare metal</a> (IPI), but instead of using bare metal nodes, for my homelab I use nested virtualization simulating bare metal nodes.</p>

<h2 id="update-18-09-2022">Update 18-09-2022</h2>
<p>Now, with the use of <strong>redfish</strong> emulator sushy-tools, by default, only one <strong>baremetal</strong> network should be used, with the advantage of removing the <strong>provisioning</strong> network.</p>

<h2 id="introduction">Introduction</h2>
<p>I use <a href="https://github.com/amedeos/ocp4-in-the-jars">Ansible playbooks</a> to install <strong>OpenShift Container Platform 4.x</strong> on a couple of (similar) Intel NUC, to test IPI bare metal installation; but instead of using bare metal nodes, I use virtual machines on NUC hosts.</p>

<p>The advantages of using this approach, is spanning resource requirements to multiple, little and usually cheaper hosts, instead of using only one, bigger host with embedded BMC; but the playbook is flexible to be used also against one, bigger host; for example, I used on Hetzner a “bigger” host to deploy all in one OpenShift master and worker nodes.</p>

<p>All OpenShift hosts will be created as a virtual machine with nested virtualization upon your NUCs.</p>

<h2 id="architecture-using-multiple-hosts">Architecture using multiple hosts</h2>

<p>In the following example, multiple hosts are used and could be added in the future, for example to add more worker nodes.</p>

<p><img src="/images/openshift/ocp4-in-the-jars-multiple-nuc.png" alt="architecture-multiple-nuc" /></p>

<h2 id="architecture-using-only-one-host">Architecture using only one host</h2>

<p>In the following example, only one host is used. For example, you can rent a dedicated server on Hetzner, with CentOS Stream 8, and running against it the playbook <a href="https://github.com/amedeos/ocp4-in-the-jars/blob/main/prepare-hypervisor.yaml">prepare-hypervisor.yaml</a> you will have a single KVM hypervisor, reachable on the internet, with iptables rules to route <code class="language-plaintext highlighter-rouge">api</code> and <code class="language-plaintext highlighter-rouge">apps</code> to OpenShift and NAT rules to allow master and worker nodes to reach “Internet”</p>

<p><img src="/images/openshift/ocp4-in-the-jars-single-nuc.png" alt="architecture-one-host" /></p>

<h2 id="requirements">Requirements</h2>
<h3 id="networks">Networks</h3>
<p>If you want to run on only one host all virtual machines, you can skip this task, otherwise, if you want to use multiple NUC hosts, you need to setup your switch with one baremetal network, where baremetal network could be a native VLAN or tagged by your NUC Linux bridge, . This is required if you use a trunked network/cable.</p>

<p>The default configuration will use these L2 and L3 settings:</p>

<table>
  <thead>
    <tr>
      <th>VLAN</th>
      <th>Name</th>
      <th>Subnet</th>
      <th>Native</th>
      <th>Bridge</th>
      <th>Gateway</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>2003</td>
      <td>Baremetal</td>
      <td>192.168.203.0/24</td>
      <td> </td>
      <td>bm</td>
      <td>192.168.203.1</td>
    </tr>
  </tbody>
</table>

<h3 id="operating-system-and-packages">Operating System and packages</h3>
<p>Your Linux NUC hosts require the following <strong>packages</strong> installed and working:</p>

<ul>
  <li>libvirt</li>
  <li>qemu</li>
  <li>nested virtualization</li>
  <li>libguestfs</li>
  <li>sushy-tools</li>
  <li>ssh</li>
</ul>

<p>there is no constraint on which Linux distribution to use. For example, I use Gentoo, but you can use RHEL 8, CentOS Stream 8, Ubuntu, Arch…</p>

<p>If you’re using CentOS Stream 8 on your NUCs, you can use the Ansible playbook <code class="language-plaintext highlighter-rouge">prepare-hypervisor.yaml</code> to properly setup your NUC(s):</p>

<ul>
  <li>Clone <a href="https://github.com/amedeos/ocp4-in-the-jars">ocp4-in-the-jars</a> repository:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone https://github.com/amedeos/ocp4-in-the-jars
<span class="nv">$ </span><span class="nb">cd </span>ocp4-in-the-jars
</code></pre></div>    </div>
  </li>
  <li>Create an Ansible inventory host for <strong>kvmhost</strong> group, where for each host you have to specify a single, free <strong>baremetal_ip</strong>, the content could be something like this:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat </span>hosts-kvmhost 
<span class="o">[</span>kvmhost]
centos01 <span class="nv">baremetal_ip</span><span class="o">=</span>192.168.203.3 <span class="nv">ansible_ssh_user</span><span class="o">=</span>root <span class="nv">ansible_ssh_common_args</span><span class="o">=</span><span class="s1">'-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'</span>
centos02 <span class="nv">baremetal_ip</span><span class="o">=</span>192.168.203.4 <span class="nv">ansible_ssh_user</span><span class="o">=</span>root <span class="nv">ansible_ssh_host</span><span class="o">=</span>192.168.201.10 <span class="nv">ansible_ssh_common_args</span><span class="o">=</span><span class="s1">'-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'</span>
</code></pre></div>    </div>
  </li>
  <li>create a <strong>custom-variables.yaml</strong> file:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">touch </span>custom-variables.yaml
</code></pre></div>    </div>
  </li>
  <li>review baremetal network in <code class="language-plaintext highlighter-rouge">variables.yaml</code> file, where, if you’re running all VMs on only one host you can leave as is, otherwise adapt to your trunked network and set new values into <strong>custom-variables.yaml</strong>; in the following example, I’ve changed bridge names, networks CIDR and MTU:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vi custom-variables.yaml
bridge_prov: br0
bridge_bm: baremetal
baremetal_net:
  net: 192.168.243.0
  netmask: 255.255.255.0
  prefix: 24
  reverse: 243.168.192
  gateway: 192.168.243.1
  ntp: <span class="s2">"103.16.182.23,103.16.182.214"</span>
  dhcp_start: 192.168.243.90
  dhcp_end: 192.168.243.110
  mtu: 3400
  vlan: 2003
</code></pre></div>    </div>
  </li>
  <li>review <strong>kvmhost</strong> variables, where, if you’re running all VMs on only one host, you can leave as is, otherwise adapt to your needs, setting new values into <strong>custom-variables.yaml</strong> file; for example if you need to configure your multiple NUC bridges with your correct L2+L3 settings change <strong>provisioning_bridge_isolated</strong> and <strong>baremetal_bridge_isolated</strong> variables from True to <strong>False</strong>, instead if you want that your NUC act as baremetal network default gateway change <strong>enable_baremetal_gw</strong> from True to <strong>False</strong>:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vi custom-variables.yaml
kvmhost:
  enable_selinux: True
  reboot_timeout: 1200
  enable_portfw: True
  replace_ddns_duckdns: False
  provisioning_bridge_create: True
  provisioning_bridge_isolated: False
  baremetal_bridge_create: True
  baremetal_bridge_isolated: False
  enable_baremetal_gw: False
  set_hostname: True
  set_hosts: True
  additional_hosts: personal_hosts.j2
  create_ssh_key: True
</code></pre></div>    </div>
  </li>
  <li>run <strong>prepare-hypervisor.yaml</strong> playbook:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ansible-playbook <span class="nt">-i</span> hosts-kvmhost <span class="nt">--extra-vars</span> <span class="s2">"@custom-variables.yaml"</span>  prepare-hypervisor.yaml
</code></pre></div>    </div>
  </li>
</ul>

<h3 id="red-hat-login-subscription-and-rhel-qcow2">Red Hat Login, Subscription and RHEL qcow2</h3>
<p>In order to run Ansible playbooks, you need to pass it your Red Hat login/password, if you don’t have it subscribe yourself to <a href="https://developers.redhat.com">Red Hat Developer Program</a></p>

<h4 id="create-a-red-hat-developer-program-membership">Create a Red Hat Developer Program membership</h4>
<ul>
  <li>go to <a href="https://developers.redhat.com">Red Hat Developer Program</a> and create a new user. Once you have done, click on tab <a href="https://access.redhat.com/management/subscriptions">Subscriptions</a> and then click on <strong>Red Hat Developer Subscription for Individuals</strong></li>
</ul>

<p><img src="/images/openshift/rhdeveloper/07-subscription.png" alt="Red Hat Subscriptions" /></p>

<ul>
  <li>click on sub tab <strong>Subscriptions</strong>:</li>
</ul>

<p><img src="/images/openshift/rhdeveloper/08-subscription.png" alt="Red Hat Subscriptions" /></p>

<ul>
  <li>
    <p>click on the <strong>Subscription number</strong> and copy the Pool ID;</p>
  </li>
  <li>
    <p>now you can fill in your <strong>custom-variables.yaml</strong> file your <strong>rh_subcription_user, rh_subcription_password</strong> and <strong>rh_subcription_pool</strong>:</p>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vi custom-variables.yaml
rh_subcription_user: &lt;YOURRHUSERNAME&gt;
rh_subcription_password: &lt;YOURRHPASSWORD&gt;
rh_subcription_pool: &lt;YOURPOOLID&gt;
</code></pre></div>    </div>
  </li>
</ul>

<h4 id="download-a-pull-secret">Download a pull-secret</h4>
<p>Now you need to download a valid pull-secret.</p>

<ul>
  <li>go to <a href="https://console.redhat.com/">Red Hat Console</a>, click on <strong>OpenShift</strong> and then click on <strong>Create cluster</strong>:</li>
</ul>

<p><img src="/images/openshift/rhdeveloper/09-console.png" alt="Red Hat Console" /></p>

<ul>
  <li>click on <strong>Datacenter</strong> tab and then on <strong>Bare Metal(x86_64)</strong> link:</li>
</ul>

<p><img src="/images/openshift/rhdeveloper/10-console.png" alt="Red Hat Console" /></p>

<ul>
  <li>click on <strong>Installer-provisioned infrastructure</strong>:</li>
</ul>

<p><img src="/images/openshift/rhdeveloper/11-console.png" alt="Red Hat Console" /></p>

<ul>
  <li>click on <strong>Copy pull secret</strong>:</li>
</ul>

<p><img src="/images/openshift/rhdeveloper/12-console.png" alt="Red Hat Console" /></p>

<p>and paste it into <strong>pull-secret.txt</strong> file, removing the last blank line:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vi pull-secret.txt
<span class="nv">$ </span><span class="nb">wc</span> <span class="nt">-l</span> pull-secret.txt 
1 pull-secret.txt
</code></pre></div></div>

<h4 id="download-rhel-86-qcow2">Download RHEL 8.6 qcow2</h4>
<p>Two virtual machines, <strong>utility</strong> and <strong>bastion</strong> are based on standard RHEL 8.6 qemu KVM file.</p>

<ul>
  <li>go to <a href="https://access.redhat.com/downloads/">Red Hat Downloads</a> and click on <strong>Red Hat Enterprise Linux</strong>:</li>
</ul>

<p><img src="/images/openshift/rhdeveloper/13-download.png" alt="Red Hat Downloads" /></p>

<ul>
  <li>select version <strong>8.6</strong>:</li>
</ul>

<p><img src="/images/openshift/rhdeveloper/14-download.png" alt="Red Hat Downloads" /></p>

<ul>
  <li>click <strong>Download Now</strong> button for <strong>Red Hat Enterprise Linux 8.6 KVM Guest Image</strong>:</li>
</ul>

<p><img src="/images/openshift/rhdeveloper/15-download.png" alt="Red Hat Downloads" /></p>

<p>remember to put this qcow2 file on all your NUC host under <strong>/root/images/rhel-8.6-x86_64-kvm.qcow2</strong>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>scp rhel-8.6-x86_64-kvm.qcow2 &lt;NUC HOST&gt;:/root/images/rhel-8.6-x86_64-kvm.qcow2
</code></pre></div></div>

<h2 id="edit-ansible-inventory">Edit Ansible inventory</h2>

<p>If you’re installing all VMs in only one host / hypervisor / NUC, skip this chapter, otherwise if you want to balance your VMs across multiple hosts, hypervisors, NUCs, you need to specify how many workers you want and on which KVM host / NUC system, every virtual machines (utility, bastion, masters and workers) will be created; for doing this you need to create a <strong>custom-bm-ansible-nodes.json</strong> file where you can specify hypervisor (NUC), <strong>redfish</strong> IP and port and MAC addresses, where redfish ip usually is the baremetal_ip defined on hosts-kvmhost inventory file.</p>

<ul>
  <li>copy from all in one file <strong>bm-ansible-nodes.json</strong>:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cp </span>bm-ansible-nodes.json custom-bm-ansible-nodes.json
</code></pre></div>    </div>
  </li>
  <li>edit <strong>custom-bm-ansible-nodes.json</strong> file with your customization:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vi custom-bm-ansible-nodes.json
</code></pre></div>    </div>
  </li>
</ul>

<p>where, for example, if you want to run <strong>master-0</strong> node (VM), on hypervisor centos01.exameple.com, establish Ansible SSH connection with <strong>root</strong> user:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">...</span><span class="w">
    </span><span class="nl">"master_nodes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"master-0"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"state"</span><span class="p">:</span><span class="w"> </span><span class="s2">"present"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"hypervisor_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"centos01.example.com"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"hypervisor_user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"root"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"hypervisor_ssh_key"</span><span class="p">:</span><span class="w"> </span><span class="s2">"~/.ssh/id_rsa"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"hypervisor_image_dir"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/var/lib/libvirt/images"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"provisioning_mac"</span><span class="p">:</span><span class="w"> </span><span class="s2">"52:54:00:00:32:00"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"baremetal_mac"</span><span class="p">:</span><span class="w"> </span><span class="s2">"52:54:00:00:33:00"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"vbmc_pre_cmd"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
            </span><span class="nl">"vbmc_ip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192.168.201.102"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"vbmc_port"</span><span class="p">:</span><span class="w"> </span><span class="s2">"623"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"redfish_ip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192.168.203.1"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"redfish_port"</span><span class="p">:</span><span class="w"> </span><span class="s2">"8000"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"baremetal_ip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"192.168.203.53"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"baremetal_last"</span><span class="p">:</span><span class="w"> </span><span class="s2">"53"</span><span class="w">
        </span><span class="p">},</span><span class="w">
</span><span class="err">...</span><span class="w">
</span></code></pre></div></div>

<h2 id="run-the-installation">Run the Installation</h2>

<p>If you haven’t created customization file variables <strong>custom-variables.yaml</strong> file, nor custom inventory <strong>custom-bm-ansible-nodes.json</strong> file, just run the main.yaml playbook:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ansible-playbook main.yaml
</code></pre></div></div>

<p>otherwise pass them as Ansible extra variable file:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ansible-playbook <span class="nt">--extra-vars</span> <span class="s2">"@custom-variables.yaml"</span> <span class="nt">--extra-vars</span> <span class="s2">"@custom-bm-ansible-nodes.json"</span> main.yaml
</code></pre></div></div>

<p>then wait 1-3 hours until the installation completes.</p>

<h2 id="post-installation-checks">Post installation checks</h2>

<p>Connect to your bastion virtual machine:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ssh kni@&lt;BASTIONIP&gt;

<span class="c"># if you haven't changed IP this should be 192.168.203.50</span>

<span class="nv">$ </span>ssh kni@192.168.203.50
</code></pre></div></div>

<p>check clusterversion:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">export </span><span class="nv">KUBECONFIG</span><span class="o">=</span>/home/kni/ocp-lab/auth/kubeconfig

<span class="nv">$ </span>oc get clusterversion
</code></pre></div></div>

<p>check cluster operator:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># output should be empty</span>
<span class="nv">$ </span>oc get co | egrep <span class="nt">-v</span> <span class="s1">'4\.[0-9]+\.[0-9]+\s+True\s+False\s+False'</span>
</code></pre></div></div>

<h2 id="optional---clean-up">Optional - Clean up</h2>

<p>If you want to clean up everything, run the <strong>cleanup.yaml</strong> playbook:</p>

<p><strong>WARNING:</strong> the following command will delete all resources created!</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ansible-playbook cleanup.yaml
</code></pre></div></div>

<p>if you have custom variable files, pass them to ansible-playbook command:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ansible-playbook <span class="nt">--extra-vars</span> <span class="s2">"@custom-variables.yaml"</span> <span class="nt">--extra-vars</span> <span class="s2">"@custom-bm-ansible-nodes.json"</span> cleanup.yaml
</code></pre></div></div>

<h2 id="optional---set-dynamic-dns-and-valid-certificate">Optional - Set dynamic dns and valid certificate</h2>

<p>If you want to update your cluster with a dynamic DNS entry, and with it, create a valid certificate for your cluster, first create a valid token and domain on <a href="https://www.duckdns.org/">Duck DNS</a>; after this, edit your custom-variables.yaml with:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">$ vi custom-variables.yaml</span>
<span class="s">....</span>
<span class="na">duckdns_token</span><span class="pi">:</span> <span class="s">YOURTOKEN</span>       <span class="c1">#### &lt;= put here your valid token on duckdns.org</span>
<span class="na">cluster_name</span><span class="pi">:</span> <span class="s">YOURDOMAINONDD</span>   <span class="c1">#### &lt;= put here your valid domain on duckdns.org</span>
<span class="na">base_domain</span><span class="pi">:</span> <span class="s">duckdns.org</span>
<span class="na">domain</span><span class="pi">:</span> <span class="s2">"</span><span class="s">YOURDOMAINONDD.duckdns.org"</span>
<span class="na">enable_ddns_duckdns</span><span class="pi">:</span> <span class="s">True</span>
<span class="na">enable_letsencrypt</span><span class="pi">:</span> <span class="s">True</span>
</code></pre></div></div>

<p>then you can run the installation.</p>]]></content><author><name></name></author><category term="OpenShift" /><category term="OpenShift" /><category term="ipi" /><summary type="html"><![CDATA[Warning: This document / project / repository / playbooks should be used only for testing OpenShift Container Platform 4.x and NOT for production environments.]]></summary></entry><entry><title type="html">Use btrbk for remote backup solution with btrfs</title><link href="/backup/2021/08/18/Use-btrbk-for-backup-on-btrfs.html" rel="alternate" type="text/html" title="Use btrbk for remote backup solution with btrfs" /><published>2021-08-18T08:00:00+00:00</published><updated>2021-08-18T08:00:00+00:00</updated><id>/backup/2021/08/18/Use-btrbk-for-backup-on-btrfs</id><content type="html" xml:base="/backup/2021/08/18/Use-btrbk-for-backup-on-btrfs.html"><![CDATA[<h2 id="introduction">Introduction</h2>
<p>In this “simple” post I’ll show you how to configure <a href="https://digint.ch/btrbk/">btrbk</a> to send to a remote Linux box your subvolume, in order to backup your data, also I’ll show you how to limit permissions to btrbk using <strong>sudo</strong> and <strong>ssh_filter_btrbk.sh</strong> script file.</p>

<p>btrbk program uses the btrfs send / receive feature, but it simplifies the management of subvolumes and the ability to send from your source box and receive in your target box the subvolumes over ssh.</p>

<h2 id="requirements">Requirements</h2>
<p>You will need:</p>

<ul>
  <li>Your source Linux box must use btrfs subvolume, in this post I’ll use the <strong>@home</strong> subvolume to backup / send to the remote box, but you could adapt to your needs and use another btrfs subvolume</li>
  <li>Your destination Linux box must have one <strong>btrfs device / file system</strong>;</li>
  <li>Ability to <strong>install btrbk</strong> on your preferred Linux distro, on both source and destination Linux boxes;</li>
  <li>Ability to create a <strong>dedicated user</strong> for backup, on both source and destination Linux boxes, in this post I’ll use <strong>backupuser</strong>;</li>
  <li>Ability to configure <strong>sudo permissions</strong> on both source and destination Linux boxes.</li>
</ul>

<h2 id="install-btrbk">Install btrbk</h2>
<p>Install btrbk on both source and destination hosts, using the package manager of your preferred distro; regarding the destination host we’ll use only the provided script <strong>ssh_filter_btrbk.sh</strong>, for this reason we’ll install also on destination host the btrbk program.</p>

<p>On Gentoo:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># emerge --ask app-backup/btrbk</span>
</code></pre></div></div>

<p>On Fedora:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># dnf install btrbk</span>
</code></pre></div></div>

<p>After the installation identifies where your distro install the <strong>ssh_filter_btrbk.sh</strong> script, on Gentoo and Fedora, this script is located on <strong>/usr/share/btrbk/scripts/ssh_filter_btrbk.sh</strong></p>

<h2 id="configure-your-source-box-to-backup">Configure your source box to backup</h2>
<h3 id="mount-btrfs-volume">Mount btrfs volume</h3>
<p>Mount your primary btrfs volume under the directory <strong>/mnt/btrbk_pool</strong>, doing this you’ll be able to backup all subvolumes</p>

<p>Create the directory:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># mkdir -p /mnt/btrbk_pool</span>
</code></pre></div></div>

<p>Identify your btrfs UUID, in my case is a000eea9-d97c-4107-ae39-602049a6acaa:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># blkid | egrep 'TYPE=\"btrfs\"' | sed -E 's/.+\s+UUID=\"([0-9a-z\-]+)\"\s+.+/\1/g'</span>
a000eea9-d97c-4107-ae39-602049a6acaa
</code></pre></div></div>

<p>Now edit your <strong>/etc/fstab</strong> in order to mount your btrfs volume under <strong>/mnt/btrbk_pool</strong>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># vi /etc/fstab</span>
<span class="c"># grep btrbk_pool /etc/fstab</span>
<span class="nv">UUID</span><span class="o">=</span>a000eea9-d97c-4107-ae39-602049a6acaa       /mnt/btrbk_pool                 btrfs           noatime,relatime,compress<span class="o">=</span>no,ssd,space_cache,discard<span class="o">=</span>async                      0 0
</code></pre></div></div>

<p><strong>NOTE 1:</strong> remove <strong>ssd</strong> option if you’re using rotational disks</p>

<p><strong>NOTE 2:</strong> remove <strong>discard=async</strong> if your’re using Kernel &lt; 5.6</p>

<p>Mount the volume and check if the subvolume <strong>@home</strong> is present:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># mount -a</span>
<span class="c"># btrfs subvolume list /mnt/btrbk_pool | egrep -E '\@home$'</span>
ID 257 gen 84832 top level 5 path @home
</code></pre></div></div>

<h3 id="create-backupuser">Create backupuser</h3>
<p>Now you can create on your source box the new user <strong>backupuser</strong>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># useradd backupuser</span>
</code></pre></div></div>

<p>Add sudo permission for backupser creating a new file /etc/sudoers.d/backupuser:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cat /etc/sudoers.d/backupuser </span>
%backupuser <span class="nv">ALL</span><span class="o">=(</span>ALL<span class="o">)</span> NOPASSWD: /sbin/btrfs, /bin/readlink, /usr/bin/readlink
</code></pre></div></div>

<h3 id="create-ssh-key">Create ssh Key</h3>
<p>Create a new ssh key, which will be trusted on the destination box:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># mkdir /etc/btrbk/ssh</span>
<span class="c"># chown backupuser. /etc/btrbk/ssh/</span>
<span class="c"># chmod 0700 /etc/btrbk/ssh</span>
<span class="c"># su - backupuser</span>
backupuser@sourcebox ~ <span class="nv">$ </span>ssh-keygen <span class="nt">-t</span> rsa <span class="nt">-b</span> 4096 <span class="nt">-f</span> /etc/btrbk/ssh/id_rsa <span class="nt">-C</span> backuser@<span class="si">$(</span><span class="nb">hostname</span><span class="si">)</span> <span class="nt">-N</span> <span class="s2">""</span>
</code></pre></div></div>

<h3 id="configure-etcbtrbkbtrbkconf">Configure /etc/btrbk/btrbk.conf</h3>
<p>In this example, I’ll backup and send to the remote Linux box only the <strong>@home</strong> subvolume, but you can adapt it based on your needs.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cat /etc/btrbk/btrbk.conf</span>
timestamp_format        long
ssh_identity /etc/btrbk/ssh/id_rsa
ssh_user backupuser

backend_remote btrfs-progs-sudo
backend btrfs-progs-sudo

snapshot_preserve_min   2d
snapshot_preserve      14d

target_preserve_min    no
target_preserve        20d 10w <span class="k">*</span>m

volume /mnt/btrbk_pool
  subvolume @home
    target ssh://&lt;FQDN&gt;/ssddata/backup/lapdog
</code></pre></div></div>

<p>Change the FQDN with your target box IP or FQDN</p>

<h2 id="configure-your-target-box-to-receive-backup">Configure your target box to receive backup</h2>
<p>Now we can configure the target box in order to receive the btrfs subvolume coming from our source box.</p>
<h3 id="create-a-new-backup-subvolume">Create a new @backup subvolume</h3>
<p>Identify your btrfs volume and create a new <strong>@backup</strong> subvolume, personally I’ve been using a luks device named <strong>“ssddata”</strong>, but you could use for example an hdd disk(s) /dev/sdX1.</p>

<p>Create a new subvolume:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># mount /dev/mapper/ssddata /mnt/ssddata</span>
<span class="c"># cd /mnt/ssddata</span>
<span class="c"># btrfs subvolume create @backup</span>
</code></pre></div></div>

<p>update your <strong>/etc/fstab</strong> with the entry for subvolume <strong>@backup</strong> mounting it under <strong>/ssddata/backup</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># mkdir -p /ssddata/backup</span>
<span class="c"># vi /etc/fstab</span>
<span class="c"># grep backup /etc/fstab</span>
<span class="nv">UUID</span><span class="o">=</span>aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee       /ssddata/backup         btrfs   noatime,relatime,compress<span class="o">=</span>lzo,ssd,space_cache,discard<span class="o">=</span>async,subvol<span class="o">=</span>@backup      0 0
</code></pre></div></div>

<p>mount the subvolume:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># mount -a</span>
</code></pre></div></div>

<p>and create the <strong>lapdog</strong> directory (if you want to change the name, remember to change it also on btrbk.conf on source box):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># mkdir -p /ssddata/backup/lapdog</span>
</code></pre></div></div>

<h3 id="create-backupuser-1">Create backupuser</h3>
<p>Now you can create on your target box the new user <strong>backupuser</strong> (same as we have done on source box :smile: ):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># useradd backupuser</span>
</code></pre></div></div>

<p>Add sudo permission for backupser creating a new file /etc/sudoers.d/backupuser:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cat /etc/sudoers.d/backupuser </span>
%backupuser <span class="nv">ALL</span><span class="o">=(</span>ALL<span class="o">)</span> NOPASSWD: /sbin/btrfs, /bin/readlink, /usr/bin/readlink
</code></pre></div></div>

<h3 id="trust-ssh-key">Trust ssh key</h3>
<p>Copy the content of the ssh pub file from your source box:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cat /etc/btrbk/ssh/id_rsa.pub</span>
</code></pre></div></div>

<p>Put the content of the file <strong>/etc/btrbk/ssh/id_rsa.pub</strong> in your clipboard and then go to your target box and run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># su - backupuser</span>
<span class="nv">$ </span><span class="nb">mkdir</span> <span class="nt">-p</span> ~/.ssh
<span class="nv">$ </span><span class="nb">chmod </span>0700 ~/.ssh
</code></pre></div></div>

<p>and the edit the file <strong>vim ~/.ssh/authorized_keys</strong> of the backupuser adding first the <em>command=”/usr/share/btrbk/scripts/ssh_filter_btrbk.sh -l –sudo –target –delete –info”</em> and then, on the same line with only a space dividing them, the content of id_rsa.pub coming from your source box;</p>

<p>below an example of the ~/.ssh/authorized_keys file:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat</span> /home/backupuser/.ssh/authorized_keys
<span class="nb">command</span><span class="o">=</span><span class="s2">"/usr/share/btrbk/scripts/ssh_filter_btrbk.sh -l --sudo --target --delete --info"</span> ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDwRPIBZMgomFVfXOyOwYm+CuSdWfWR7tMIh+aJgWGv1pK8zuTiZtoaCSnobrRVJNkWWNIeL672o9zgn8y5N2nb64pWxDCcJWFKHuxCZk3ZN1i70JPTZ25sUZ0YUQ8YCd4YtLIujPdIdCMNTESrB0QYe0CCyD6HnX2DRR36G3EVRbNmBpzeLLthIoZLzRpGXFeHMLIz3W9v5VrIwDYZGWdUptyqbh9YQd7x9+lqmaCSlAzRttMVk6HiH8hUuJLgseNtvamqqsEQcZGk3j4v3EbYR+oCQqb4njcxQ3YbPuKtc88PREIezNt/rcoo4m720nXOeKZCad5Ob0/gd9CnBPY3xo8Po1UZdOSrvUxr46moAhMMBVy8c9LO32AlJ7oKjgt2UFelOdWlx69vCZ7TezYRCSj5DS2ZtlYe4KN1pRfLwe1h+h/tt4QVMmKpbl771VKaTzb1xM3TwR8SSXRqct/NeXGWNm7CPrsPx1qK6NFsqx0KH/Wc93uIqbucC5fUhd7rc5yYX43yO3vDon+Omlc9OIAmfxtssTK8/XU7C9fQDMACgwcFxh7JixdPzqlVvJxNiJiSjpWSkixXubBRwgWlTf/L7hZppFcnS0j+ZtgsTiBofm5QiMHskQIUoZ0WmdyuzwJQVQMBV58rZdB7goxvLaW63SM/b6FVk5c0m9dV4w<span class="o">==</span> backuser@lapdog
</code></pre></div></div>

<h2 id="run-your-first-backup-and-send-it-via-ssh-to-target-box">Run your first backup and send it via ssh to target box</h2>
<p>Now you can run the first backup using btrbk and it will automagically send the btrfs subvolumes through ssh to the target box:</p>

<p>From your source box switch to backupuser and run it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># su - backupuser</span>
<span class="nv">$ </span>btrbk <span class="nt">-c</span> /etc/btrbk/btrbk.conf <span class="nt">-v</span> run
</code></pre></div></div>

<p>after it ends, you can run the <strong>list all</strong> command in order to see all backups:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># su - backupuser</span>
<span class="nv">$ </span>btrbk <span class="nt">-c</span> /etc/btrbk/btrbk.conf <span class="nt">-n</span> list all
</code></pre></div></div>]]></content><author><name></name></author><category term="backup" /><category term="backup" /><category term="btrfs" /><category term="btrbk" /><summary type="html"><![CDATA[Introduction In this “simple” post I’ll show you how to configure btrbk to send to a remote Linux box your subvolume, in order to backup your data, also I’ll show you how to limit permissions to btrbk using sudo and ssh_filter_btrbk.sh script file.]]></summary></entry><entry><title type="html">Configure Gentoo to unlock LUKS root file system with FIDO2 key</title><link href="/gentoo/2021/04/25/Unlock-rootfs-with-fido2-key.html" rel="alternate" type="text/html" title="Configure Gentoo to unlock LUKS root file system with FIDO2 key" /><published>2021-04-25T08:00:00+00:00</published><updated>2021-04-25T08:00:00+00:00</updated><id>/gentoo/2021/04/25/Unlock-rootfs-with-fido2-key</id><content type="html" xml:base="/gentoo/2021/04/25/Unlock-rootfs-with-fido2-key.html"><![CDATA[<h2 id="introduction">Introduction</h2>
<p>In this post I’ll describe how to unlock your <strong>LUKS</strong> device, which contains the root file system, using a <strong>FIDO2</strong> hardware key.</p>

<p><strong>UPDATE: 03/08/2023</strong>: Update /etc/dracut.conf.d/fido2.conf file with new systemd’ fido2 requirement</p>

<h2 id="requirements">Requirements</h2>
<p>In order to unlock your LUKS device at boot using your FIDO2 hardware key your Gentoo box need to meet these conditions:</p>

<ul>
  <li>only <strong>LUKS2</strong> device is supported, if your device is LUKS1 you can’t unlock it with FIDO2 key; to check if your device is LUKS2 you can simply run a cryptsetup status command:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cryptsetup status &lt;YOUR LUKS DEVICE&gt; | grep type</span>
<span class="nb">type</span>: LUKS2
</code></pre></div>    </div>
  </li>
  <li>systemd version <strong>248</strong> or higher;
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># qlist -Idv | grep sys-apps/systemd</span>
sys-apps/systemd-248
</code></pre></div>    </div>
  </li>
  <li>FIDO2 hardware key; for this I bought the <a href="https://solokeys.com/collections/all/products/solo-usb-a">Solokeys Solo USB-A</a> but I hope you can use any FIDO2 available in the market;</li>
  <li><strong>sys-kernel/dracut</strong> will be used for building the initial ram file system.</li>
</ul>

<h2 id="software-installation">Software installation</h2>
<h3 id="sys-appssystemd">sys-apps/systemd</h3>
<p>Recently, Gentoo systemd mantainer has added fido2 support, just add fido2 USE flag:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># mkdir -p /etc/portage/package.use</span>
<span class="c"># echo "sys-apps/systemd fido2" &gt;&gt; /etc/portage/package.use/systemd</span>
</code></pre></div></div>

<p>after enabling fido2 USE flag re-emerge systemd:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># time emerge --ask --verbose --update --deep --with-bdeps=y --newuse  --keep-going --autounmask-write=y --backtrack=30  @world</span>
</code></pre></div></div>

<p>check if your systemd has FIDO2 capability:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># systemctl --version | grep FIDO | sed -E 's/.+([+-]FIDO2).+/\1/'</span>
+FIDO2
</code></pre></div></div>
<p>reboot the system in order to use your new systemd:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># reboot</span>
</code></pre></div></div>
<h3 id="sys-kerneldracut">sys-kernel/dracut</h3>
<p>Install dracut if you haven’t done before:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># emerge --ask sys-kernel/dracut</span>
</code></pre></div></div>
<h2 id="enroll-your-fido2-key-in-your-luks-device">Enroll your FIDO2 key in your LUKS device</h2>
<p>Now you can plug your FIDO2 hardware token and enroll a FIDO2 key on your LUKS device, in my case the device is /dev/nvme0n1p3, but change accordingly to your environment (for example /dev/sda3):</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># systemd-cryptenroll --fido2-device=auto /dev/nvme0n1p3 </span>
🔐 Please enter current passphrase <span class="k">for </span>disk /dev/nvme0n1p3: <span class="o">(</span>no <span class="nb">echo</span><span class="o">)</span>               
Initializing FIDO2 credential on security token.
👆 <span class="o">(</span>Hint: This might require verification of user presence on security token.<span class="o">)</span>
Generating secret key on FIDO2 security token.
New FIDO2 token enrolled as key slot 1.
</code></pre></div></div>
<p>when requested type your current passphrase for your LUKS device, and then, in case you’re using Solokeys, press the button on the token when the led becomes red</p>

<p>If you want to check if your LUKS device now has the FIDO2 key you can run:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cryptsetup luksDump /dev/nvme0n1p3</span>
...
Tokens:
  0: systemd-fido2
        Keyslot:  1
...
</code></pre></div></div>
<h2 id="configure-etccrypttab">Configure /etc/crypttab</h2>
<p>Configure your <strong>/etc/crypttab</strong> to point to your LUKS device, but giving a <strong>human</strong> name like <strong>rootvolume</strong>, instead of classical UUID:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cat /etc/crypttab </span>
rootvolume /dev/nvme0n1p3 - fido2-device<span class="o">=</span>auto
</code></pre></div></div>
<h2 id="configure-dracut">Configure dracut</h2>
<p>Dracut, by default doesn’t install libfido2 and crypttab file, for this you can create a new file <strong>/etc/dracut.conf.d/fido2.conf</strong> with the following contents:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cat /etc/dracut.conf.d/fido2.conf </span>
add_dracutmodules+<span class="o">=</span><span class="s2">" fido2 "</span>
install_items+<span class="o">=</span><span class="s2">" /etc/crypttab "</span>
</code></pre></div></div>

<p>now you can rebuild your initial ram file system for your current kernel:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># dracut --force --kver $(uname -r)</span>
</code></pre></div></div>
<p>instead, if you want to rebuild it for a specific kernel, for example <strong>5.11.16-gentoo-amedeo05</strong>, run this command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># dracut --force --kver 5.11.16-gentoo-amedeo05</span>
</code></pre></div></div>
<h2 id="configure-grub">Configure grub</h2>
<p>Edit your grub <strong>GRUB_CMDLINE_LINUX</strong> parameter removing all luks options which refer to your LUKS UUID, you should do this in order to make easier the boot procedure without the FIDO2 key; your GRUB_CMDLINE_LINUX should be something like this:</p>

<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">GRUB_CMDLINE_LINUX</span><span class="p">=</span><span class="s">"init=/usr/lib/systemd/systemd rd.luks.allow-discards root=UUID=a000eea9-d97c-4107-ae39-602049a6acaa rootflags=subvol=@"</span>
</code></pre></div></div>

<p><strong>Note:</strong> the above root=UUID refers to my btrfs UUID and not to the LUKS UUID</p>

<p>now you can run the <strong>grub-mkconfig</strong>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># grub-mkconfig -o /boot/grub/grub.cfg</span>
</code></pre></div></div>
<h2 id="reboot">Reboot</h2>
<p>Plug your FIDO2 key and reboot, and check if your newly initial ram file system can unlock your root file system:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># reboot</span>
</code></pre></div></div>
<h2 id="bonus-unlock-your-root-file-system-using-the-passphrase">(Bonus) Unlock your root file system using the passphrase</h2>
<p>If you lose, or forget your FIDO2 key you can boot your system using a LiveCD, or using the following trick.</p>

<p>When grub starts, press <strong><em>e</em></strong> to edit the boot parameter, then go to the <strong>linux</strong> entry and after all your boot parameters (in my case init=/usr/lib/systemd/systemd rd.luks.allow-discards root=UUID=a000eea9-d97c-4107-ae39-602049a6acaa rootflags=subvol=@) add the following:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rd.break<span class="o">=</span>initqueue
</code></pre></div></div>
<p>Press <strong>Ctrl+x</strong> or <strong>F10</strong> and wait for the following messages:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Press Enter <span class="k">for </span>maintenance
<span class="o">(</span>or press Control-D to <span class="k">continue</span><span class="o">)</span>:
</code></pre></div></div>
<p>now press <strong>Enter</strong> and at prompt first mask your systemd service systemd-cryptsetup@rootvolume.service:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># systemctl mask systemd-cryptsetup@rootvolume.service</span>
</code></pre></div></div>
<p>mount your rootvolume by typing your passphrase:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># /lib/systemd/systemd-cryptsetup attach rootvolume /dev/nvme0n1p3</span>
</code></pre></div></div>
<p>now you can press <strong>Control-D</strong> to tell systemd to continue the boot process.</p>]]></content><author><name></name></author><category term="gentoo" /><category term="gentoo" /><category term="luks" /><category term="systemd" /><summary type="html"><![CDATA[Introduction In this post I’ll describe how to unlock your LUKS device, which contains the root file system, using a FIDO2 hardware key.]]></summary></entry><entry><title type="html">How to install Gentoo with UEFI LUKS Btrfs and systemd</title><link href="/gentoo/2020/12/26/install-gentoo-with-uefi-luks-btrfs-and-systemd.html" rel="alternate" type="text/html" title="How to install Gentoo with UEFI LUKS Btrfs and systemd" /><published>2020-12-26T08:00:00+00:00</published><updated>2020-12-26T08:00:00+00:00</updated><id>/gentoo/2020/12/26/install-gentoo-with-uefi-luks-btrfs-and-systemd</id><content type="html" xml:base="/gentoo/2020/12/26/install-gentoo-with-uefi-luks-btrfs-and-systemd.html"><![CDATA[<h2 id="introduction">Introduction</h2>
<p>In this post I’ll describe how to install <a href="https://gentoo.org/">Gentoo</a> with <strong>systemd</strong> stage3 tarball on <strong>UEFI</strong>  <strong>LUKS</strong> partition and <strong>Btrfs</strong> filesystem, using the standard de facto <strong>@ subvolume</strong> as root file system.</p>

<p>I’ve also written two different guides to install Gentoo on LUKS, but using LVM Volume group, and ext4 filesystem, if you’re interested in those you can find <a href="https://amedeos.github.io/gentoo/2019/01/14/install-gentoo-with-luks-lvm-and-systemd.html">here</a> a guide to install on BIOS partition, and <a href="https://amedeos.github.io/gentoo/2019/01/14/install-gentoo-with-luks-lvm-and-systemd.html">here</a> a guide to install on UEFI partition.</p>

<p><strong>UPDATE 23/08/2023</strong>: Correct typo on GRUB_PLATFORMS</p>

<h2 id="disk-partitions">Disk partitions</h2>
<p>I’m using to create gpt partitions, with a small <a href="https://en.wikipedia.org/wiki/BIOS_boot_partition">BIOS boot partition</a> (2 MiB) to be used by grub for second stages of itself.</p>
<h3 id="partition-scheme">Partition scheme</h3>
<p>This is the quite simple partition scheme used in this guide.
In this guide I’m using an <strong>NVME</strong> disk <em>/dev/nvme0n1</em>, but if you have a scsi disk like <em>/dev/sda</em>, its simple as run <em>sed ‘s/nvme0n1/sda/g’</em></p>

<table>
  <tbody>
    <tr>
      <td><strong>Partition</strong></td>
      <td><strong>Filesystem</strong></td>
      <td><strong>Size</strong></td>
      <td><strong>Description</strong></td>
    </tr>
    <tr>
      <td>/dev/nvme0n1p1</td>
      <td>fat</td>
      <td>2MiB</td>
      <td>bios boot</td>
    </tr>
    <tr>
      <td>/dev/nvme0n1p2</td>
      <td>fat32</td>
      <td>800MiB</td>
      <td>Boot partition</td>
    </tr>
    <tr>
      <td>/dev/nvme0n1p3</td>
      <td>LUKS</td>
      <td>rest of the disk</td>
      <td>LUKS partition</td>
    </tr>
  </tbody>
</table>

<h3 id="creating-the-partitions">Creating the partitions</h3>
<p>Using <strong>parted</strong> utility now we can create all required partitions</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd ~# parted /dev/nvme0n1 
GNU Parted 3.3
Using /dev/nvme0n1
Welcome to GNU Parted! Type <span class="s1">'help'</span> to view a list of commands.
<span class="o">(</span>parted<span class="o">)</span> print 
Error: /dev/nvme0n1: unrecognised disk label
Model: WDC PC SN730 SDBQNTY-256G-1001 <span class="o">(</span>nvme<span class="o">)</span>                              
Disk /dev/nvme0n1: 256GB
Sector size <span class="o">(</span>logical/physical<span class="o">)</span>: 512B/512B
Partition Table: unknown
Disk Flags: 

<span class="o">(</span>parted<span class="o">)</span> mklabel gpt                                                      

<span class="o">(</span>parted<span class="o">)</span> mkpart primary fat32 1MiB 3MiB                                   

<span class="o">(</span>parted<span class="o">)</span> mkpart primary fat32 3MiB 803MiB

<span class="o">(</span>parted<span class="o">)</span> mkpart primary 803MiB <span class="nt">-1</span>                                         

<span class="o">(</span>parted<span class="o">)</span> name 1 grub                                                      
<span class="o">(</span>parted<span class="o">)</span> name 2 boot                                                      
<span class="o">(</span>parted<span class="o">)</span> name 3 luks                                                      

<span class="o">(</span>parted<span class="o">)</span> <span class="nb">set </span>1 bios_grub on
<span class="o">(</span>parted<span class="o">)</span> <span class="nb">set </span>2 boot on                                                    

<span class="o">(</span>parted<span class="o">)</span> print                                                            
Model: WDC PC SN730 SDBQNTY-256G-1001 <span class="o">(</span>nvme<span class="o">)</span>
Disk /dev/nvme0n1: 256GB
Sector size <span class="o">(</span>logical/physical<span class="o">)</span>: 512B/512B
Partition Table: gpt
Disk Flags: 

Number  Start   End     Size    File system  Name  Flags
 1      1049kB  3146kB  2097kB  fat32        grub  bios_grub
 2      3146kB  842MB   839MB   fat32        boot  boot, esp
 3      842MB   256GB   255GB                luks

<span class="o">(</span>parted<span class="o">)</span> quit
</code></pre></div></div>
<h2 id="create-fat-filesystems">Create fat filesystems</h2>
<p>For both bios boot we’ll create a fat filesystem, instead, for boot partition we’ll create a fat32 filesystem:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd ~# mkfs.vfat /dev/nvme0n1p1 
mkfs.fat 4.1 <span class="o">(</span>2017-01-24<span class="o">)</span>
livecd ~# mkfs.vfat <span class="nt">-F32</span> /dev/nvme0n1p2 
mkfs.fat 4.1 <span class="o">(</span>2017-01-24<span class="o">)</span>
</code></pre></div></div>
<h2 id="encrypt-partition-with-luks">Encrypt partition with LUKS</h2>
<p>Now we can crypt the third partition /dev/nvme0n1p3 with LUKS</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd ~# cryptsetup luksFormat <span class="nt">-c</span> aes-xts-plain64 <span class="nt">-s</span> 512 /dev/nvme0n1p3

WARNING!
<span class="o">========</span>
This will overwrite data on /dev/nvme0n1p3 irrevocably.

Are you sure? <span class="o">(</span>Type <span class="s1">'yes'</span> <span class="k">in </span>capital letters<span class="o">)</span>: YES
Enter passphrase <span class="k">for</span> /dev/nvme0n1p3: 
Verify passphrase: 
livecd ~# 
</code></pre></div></div>
<p>Open the LUKS device</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd ~# cryptsetup luksOpen /dev/nvme0n1p3 luksdev
Enter passphrase <span class="k">for</span> /dev/nvme0n1p3: 
livecd ~#
</code></pre></div></div>
<h2 id="create-btrfs-filesystem-and-subvolume">Create Btrfs filesystem and subvolume</h2>
<p>I’ll create this Btrfs volume/filesytem and subvolumes</p>

<table>
  <tbody>
    <tr>
      <td><strong>Volume</strong></td>
      <td><strong>Subvolume name</strong></td>
      <td><strong>Parent Volume</strong></td>
      <td><strong>Mount point</strong></td>
      <td><strong>Description</strong></td>
    </tr>
    <tr>
      <td>/dev/nvme0n1p3</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
      <td>Btrfs primary volume / device</td>
    </tr>
    <tr>
      <td>-</td>
      <td>@</td>
      <td>/dev/nvme0n1p3</td>
      <td>/</td>
      <td>Root filesystem</td>
    </tr>
    <tr>
      <td>-</td>
      <td>@home</td>
      <td>/dev/nvme0n1p3</td>
      <td>/home</td>
      <td>home filesystem</td>
    </tr>
    <tr>
      <td>-</td>
      <td>@snapshots</td>
      <td>/dev/nvme0n1p3</td>
      <td>/.snapshots</td>
      <td>snapshots filesystem</td>
    </tr>
  </tbody>
</table>

<p>Create the Btrfs filesystem with <strong>Gentoo label</strong> and mount under <strong>/mnt/gentoo</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd ~# mkfs.btrfs <span class="nt">-L</span> Gentoo /dev/mapper/luksdev 
btrfs-progs v5.4.1 
See http://btrfs.wiki.kernel.org <span class="k">for </span>more information.

Detected a SSD, turning off metadata duplication.  Mkfs with <span class="nt">-m</span> dup <span class="k">if </span>you want to force metadata duplication.
Label:              Gentoo
UUID:               a000eea9-d97c-4107-ae39-602049a6acaa
Node size:          16384
Sector size:        4096
Filesystem size:    237.67GiB
Block group profiles:
  Data:             single            8.00MiB
  Metadata:         single            8.00MiB
  System:           single            4.00MiB
SSD detected:       <span class="nb">yes
</span>Incompat features:  extref, skinny-metadata
Checksum:           crc32c
Number of devices:  1
Devices:
   ID        SIZE  PATH
    1   237.67GiB  /dev/mapper/luksdev

livecd ~# mount /dev/mapper/luksdev /mnt/gentoo
</code></pre></div></div>
<p>Create all subvolumes:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd ~# btrfs subvolume create /mnt/gentoo/@
Create subvolume <span class="s1">'/mnt/gentoo/@'</span>

livecd ~# btrfs subvolume create /mnt/gentoo/@home
Create subvolume <span class="s1">'/mnt/gentoo/@home'</span>

livecd ~# btrfs subvolume create /mnt/gentoo/@snapshots
Create subvolume <span class="s1">'/mnt/gentoo/@snapshots'</span>

livecd ~# btrfs subvolume list /mnt/gentoo
ID 256 gen 7 top level 5 path @
ID 257 gen 8 top level 5 path @home
ID 258 gen 9 top level 5 path @snapshots
livecd ~#
</code></pre></div></div>
<p><strong>umount</strong> /mnt/gentoo, because for root filesystem we’ll use the <strong>@</strong> subvolume:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd ~# umount /mnt/gentoo
</code></pre></div></div>
<p>and finally we can mount our root filesystem based on @ subvolume:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd ~#  mount <span class="nt">-t</span> btrfs <span class="nt">-o</span> noatime,relatime,compress<span class="o">=</span>lzo,ssd,space_cache,subvol<span class="o">=</span>@ /dev/mapper/luksdev /mnt/gentoo
</code></pre></div></div>

<p><strong>WARNING</strong>: if you have a recent kernel, for example 5.19 space_cache option must be <strong>space_cache=v2</strong>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd ~#  mount <span class="nt">-t</span> btrfs <span class="nt">-o</span> noatime,relatime,compress<span class="o">=</span>lzo,ssd,space_cache<span class="o">=</span>v2,subvol<span class="o">=</span>@ /dev/mapper/luksdev /mnt/gentoo
</code></pre></div></div>

<h2 id="gentoo-installation">Gentoo installation</h2>
<p>Now it’s time to get your hands dirty.</p>
<h3 id="install-systemd-stage3">Install systemd stage3</h3>
<p>Download the systemd stage3 tarball from <a href="https://gentoo.org/downloads/">Gentoo</a></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd ~# <span class="nb">cd</span> /mnt/gentoo/
livecd /mnt/gentoo <span class="c"># wget https://bouncer.gentoo.org/fetch/root/all/releases/amd64/autobuilds/20201222T005811Z/stage3-amd64-systemd-20201222T005811Z.tar.xz</span>
</code></pre></div></div>
<p>Unarchive the tarball</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd /mnt/gentoo <span class="c"># tar xpvf stage3-*.tar.xz --xattrs-include='*.*' --numeric-owner</span>
</code></pre></div></div>
<h3 id="configuring-compile-options">Configuring compile options</h3>
<p>Open /mnt/gentoo/etc/portage/make.conf file and configure the system with your preferred optimization variables. Have a look at <a href="https://wiki.gentoo.org/wiki/Handbook:AMD64/Full/Installation#Configuring_compile_options">Gentoo Handbook</a></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd /mnt/gentoo <span class="c"># vi /mnt/gentoo/etc/portage/make.conf</span>
</code></pre></div></div>
<p>For example, below you can find my make.conf optimization variables</p>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># These settings were set by the catalyst build script that automatically
# built this stage.
# Please consult /usr/share/portage/config/make.conf.example for a more
# detailed example.
</span><span class="py">COMMON_FLAGS</span><span class="p">=</span><span class="s">"-march=skylake -O2 -pipe"</span>
<span class="py">CFLAGS</span><span class="p">=</span><span class="s">"${COMMON_FLAGS}"</span>
<span class="py">CXXFLAGS</span><span class="p">=</span><span class="s">"${COMMON_FLAGS}"</span>
<span class="py">FCFLAGS</span><span class="p">=</span><span class="s">"${COMMON_FLAGS}"</span>
<span class="py">FFLAGS</span><span class="p">=</span><span class="s">"${COMMON_FLAGS}"</span>

<span class="py">CPU_FLAGS_X86</span><span class="p">=</span><span class="s">"aes avx avx2 f16c fma3 mmx mmxext pclmul popcnt sse sse2 sse3 sse4_1 sse4_2 ssse3"</span>

<span class="py">MAKEOPTS</span><span class="p">=</span><span class="s">"-j8"</span>

<span class="py">GRUB_PLATFORMS</span><span class="p">=</span><span class="s">"efi-64"</span>

<span class="py">L10N</span><span class="p">=</span><span class="s">"en it"</span>

<span class="py">USE</span><span class="p">=</span><span class="s">"-gtk -gnome systemd networkmanager pulseaudio spice usbredir udisks offensive cryptsetup ocr bluetooth bash-completion opengl opencl vulkan v4l x265 theora policykit vaapi vdpau lto cec cameras_ptp2 wayland"</span>

<span class="py">POLICY_TYPES</span><span class="p">=</span><span class="s">"targeted"</span>
<span class="py">INPUT_DEVICES</span><span class="p">=</span><span class="s">"libinput"</span>

<span class="py">ACCEPT_KEYWORDS</span><span class="p">=</span><span class="s">"~amd64"</span>
<span class="py">ACCEPT_LICENSE</span><span class="p">=</span><span class="s">"* -@EULA"</span>

<span class="py">VIDEO_CARDS</span><span class="p">=</span><span class="s">"intel i965 iris"</span>

<span class="py">LLVM_TARGETS</span><span class="p">=</span><span class="s">"BPF WebAssembly"</span>

<span class="py">PYTHON_TARGETS</span><span class="p">=</span><span class="s">"python2_7 python3_7 python3_8 python3_9"</span>

<span class="c"># NOTE: This stage was built with the bindist Use flag enabled
</span><span class="py">PORTDIR</span><span class="p">=</span><span class="s">"/var/db/repos/gentoo"</span>
<span class="py">DISTDIR</span><span class="p">=</span><span class="s">"/var/cache/distfiles"</span>
<span class="py">PKGDIR</span><span class="p">=</span><span class="s">"/var/cache/binpkgs"</span>

<span class="c"># This sets the language of build output to English.
# Please keep this setting intact when reporting bugs.
</span><span class="py">LC_MESSAGES</span><span class="p">=</span><span class="s">C</span>
</code></pre></div></div>
<h3 id="chrooting">Chrooting</h3>
<p>Copy DNS configurations:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd /mnt/gentoo <span class="c"># cp --dereference /etc/resolv.conf /mnt/gentoo/etc/</span>
</code></pre></div></div>
<p>Mount proc, dev and shm filesystems:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd /mnt/gentoo <span class="c"># mount -t proc /proc /mnt/gentoo/proc</span>
livecd /mnt/gentoo <span class="c"># mount --rbind /sys /mnt/gentoo/sys</span>
livecd /mnt/gentoo <span class="c"># mount --make-rslave /mnt/gentoo/sys</span>
livecd /mnt/gentoo <span class="c"># mount --rbind /dev /mnt/gentoo/dev</span>
livecd /mnt/gentoo <span class="c"># mount --make-rslave /mnt/gentoo/dev</span>
livecd /mnt/gentoo <span class="c"># test -L /dev/shm &amp;&amp; rm /dev/shm &amp;&amp; mkdir /dev/shm</span>
livecd /mnt/gentoo <span class="c"># mount -t tmpfs -o nosuid,nodev,noexec shm /dev/shm</span>
livecd /mnt/gentoo <span class="c"># chmod 1777 /dev/shm</span>
</code></pre></div></div>
<p>chroot to /mnt/gentoo:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>livecd /mnt/gentoo <span class="c"># chroot /mnt/gentoo /bin/bash </span>
livecd / <span class="c"># source /etc/profile</span>
livecd / <span class="c"># export PS1="(chroot) $PS1"</span>
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd / <span class="c">#</span>
</code></pre></div></div>
<p>mounting the boot partition:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd / <span class="c"># mount /dev/nvme0n1p2 /boot</span>
</code></pre></div></div>
<p>and then mount <strong>/home</strong> and <strong>/.snapshots</strong> subvolumes:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd / <span class="c"># mkdir /.snapshots</span>

<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd / <span class="c"># mount -t btrfs -o noatime,relatime,compress=lzo,ssd,subvol=@snapshots /dev/mapper/luksdev /.snapshots</span>

<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd / <span class="c"># mount -t btrfs -o noatime,relatime,compress=lzo,ssd,subvol=@home /dev/mapper/luksdev /home</span>
</code></pre></div></div>

<h3 id="updating-the-gentoo-ebuild-repository">Updating the Gentoo ebuild repository</h3>
<p>Update the Gentoo ebuild repository to the latest version:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# emerge <span class="nt">--sync</span>
</code></pre></div></div>
<h4 id="update-portage">Update portage</h4>
<p>If at the end of emerge sync you see a message like this:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">Action: sync for repo: gentoo, returned code = 0

 * An update to portage is available. It is _highly_ recommended
 * that you update portage now, before any other packages are updated.

 * To update portage, run 'emerge --oneshot sys-apps/portage' now.
</span></code></pre></div></div>
<p>if you are on this case emerge the portage:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# emerge <span class="nt">--oneshot</span> sys-apps/portage
</code></pre></div></div>
<h3 id="choosing-the-right-profile-with-systemd">Choosing the right profile (with systemd)</h3>
<p>Choose one of the systemd profile available, for example for my system I have selected desktop/plasma/systemd</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~ <span class="c"># eselect profile list</span>
...
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~ <span class="c"># eselect profile set 9</span>
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~ <span class="c"># eselect profile list</span>
Available profile symlink targets:
...
  <span class="o">[</span>9]   default/linux/amd64/17.1/desktop/plasma/systemd <span class="o">(</span>stable<span class="o">)</span> <span class="k">*</span>
...
</code></pre></div></div>
<h3 id="timezone">Timezone</h3>
<p>Update the timezone, for example Europe/Rome</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# <span class="nb">echo </span>Europe/Rome <span class="o">&gt;</span> /etc/timezone
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# emerge <span class="nt">--config</span> sys-libs/timezone-data
</code></pre></div></div>
<h3 id="configure-locales">Configure locales</h3>
<p>If you want only few locales on your system, for example C, en_us and it_IT</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd /etc <span class="c"># cat locale.gen </span>
...
C.UTF8 UTF-8
en_US ISO-8859-1
en_US.UTF-8 UTF-8
it_IT ISO-8859-1
it_IT.UTF-8 UTF-8

<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd /etc <span class="c"># locale-gen</span>
</code></pre></div></div>
<p>now you can choose your preferred locale with</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd /etc <span class="c"># eselect locale list</span>
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd /etc <span class="c"># eselect locale set 10</span>
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd /etc <span class="c"># eselect locale list</span>
...
<span class="o">[</span>10]  C.UTF8 <span class="k">*</span>
</code></pre></div></div>
<p>reload the environment</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd /etc <span class="c"># env-update &amp;&amp; source /etc/profile &amp;&amp; export PS1="(chroot) $PS1"</span>
</code></pre></div></div>
<h3 id="updating-the-world">Updating the world</h3>
<p>If you change your profile, or if you change your USE flags run the update</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# <span class="nb">mkdir</span> <span class="nt">-p</span> /etc/portage/package.<span class="o">{</span>accept_keywords,license,mask,unmask,use<span class="o">}</span>
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# <span class="nb">time </span>emerge <span class="nt">--ask</span> <span class="nt">--verbose</span> <span class="nt">--update</span> <span class="nt">--deep</span> <span class="nt">--with-bdeps</span><span class="o">=</span>y <span class="nt">--newuse</span>  <span class="nt">--keep-going</span> <span class="nt">--backtrack</span><span class="o">=</span>30 @world
</code></pre></div></div>
<p>now you can take a coffee :coffee::coffee::coffee:</p>

<h3 id="optional---gcc-upgrade">Optional - GCC Upgrade</h3>
<p>If you are in amd64 testing, most probably, updating the world, you have installed a new version of GCC, so from now we can use it</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# gcc-config <span class="nt">--list-profiles</span>
 <span class="o">[</span>1] x86_64-pc-linux-gnu-9.3.0 <span class="k">*</span>
 <span class="o">[</span>2] x86_64-pc-linux-gnu-10.2.0
</code></pre></div></div>
<p>set the default profile to 2, corresponding to the above example to the GCC 10.2</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# gcc-config 2
 <span class="k">*</span> Switching native-compiler to x86_64-pc-linux-gnu-10.2.0 ...
<span class="o">&gt;&gt;&gt;</span> Regenerating /etc/ld.so.cache...                                                                                                                                                                                                                                    <span class="o">[</span> ok <span class="o">]</span>

 <span class="k">*</span> If you intend to use the gcc from the new profile <span class="k">in </span>an already
 <span class="k">*</span> running shell, please remember to <span class="k">do</span>:

 <span class="k">*</span>   <span class="nb">.</span> /etc/profile

<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# <span class="nb">source</span> /etc/profile
livecd ~# <span class="nb">export </span><span class="nv">PS1</span><span class="o">=</span><span class="s2">"(chroot) </span><span class="nv">$PS1</span><span class="s2">"</span>
</code></pre></div></div>
<p>after that re-emerge the libtool:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# emerge <span class="nt">--ask</span> <span class="nt">--oneshot</span> <span class="nt">--usepkg</span><span class="o">=</span>n sys-devel/libtool
</code></pre></div></div>
<h3 id="optional---install-vim">Optional - Install vim</h3>
<p>If you hate nano editor like me, now you can install vim:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# emerge <span class="nt">--ask</span> app-editors/vim
</code></pre></div></div>
<h2 id="configure-fstab">Configure fstab</h2>
<p>This file contains the mount points of partitions to be mounted.
Run blkid to see the UUIDs:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# blkid 
/dev/loop0: <span class="nv">TYPE</span><span class="o">=</span><span class="s2">"squashfs"</span>
/dev/nvme0n1p1: <span class="nv">SEC_TYPE</span><span class="o">=</span><span class="s2">"msdos"</span> <span class="nv">UUID</span><span class="o">=</span><span class="s2">"1C3B-C680"</span> <span class="nv">BLOCK_SIZE</span><span class="o">=</span><span class="s2">"512"</span> <span class="nv">TYPE</span><span class="o">=</span><span class="s2">"vfat"</span> <span class="nv">PARTLABEL</span><span class="o">=</span><span class="s2">"grub"</span> <span class="nv">PARTUUID</span><span class="o">=</span><span class="s2">"39bd8ab8-3708-4ecf-b4e3-f5714a6e4ea1"</span>
/dev/nvme0n1p2: <span class="nv">UUID</span><span class="o">=</span><span class="s2">"1D97-3854"</span> <span class="nv">BLOCK_SIZE</span><span class="o">=</span><span class="s2">"512"</span> <span class="nv">TYPE</span><span class="o">=</span><span class="s2">"vfat"</span> <span class="nv">PARTLABEL</span><span class="o">=</span><span class="s2">"boot"</span> <span class="nv">PARTUUID</span><span class="o">=</span><span class="s2">"b44718e2-d7fe-4eba-a6c7-1d1beee11806"</span>
/dev/nvme0n1p3: <span class="nv">UUID</span><span class="o">=</span><span class="s2">"1ce717f4-5a82-49e7-ae1c-9a92e4c20251"</span> <span class="nv">TYPE</span><span class="o">=</span><span class="s2">"crypto_LUKS"</span> <span class="nv">PARTLABEL</span><span class="o">=</span><span class="s2">"luks"</span> <span class="nv">PARTUUID</span><span class="o">=</span><span class="s2">"c271c93e-6f59-446f-9139-a0b98afab820"</span>
/dev/sda1: <span class="nv">BLOCK_SIZE</span><span class="o">=</span><span class="s2">"2048"</span> <span class="nv">UUID</span><span class="o">=</span><span class="s2">"2020-12-22-12-44-06-70"</span> <span class="nv">LABEL</span><span class="o">=</span><span class="s2">"Gentoo amd64 20201222T005811Z"</span> <span class="nv">TYPE</span><span class="o">=</span><span class="s2">"iso9660"</span> <span class="nv">PTUUID</span><span class="o">=</span><span class="s2">"7437c9e0"</span> <span class="nv">PTTYPE</span><span class="o">=</span><span class="s2">"dos"</span> <span class="nv">PARTUUID</span><span class="o">=</span><span class="s2">"7437c9e0-01"</span>
/dev/sda2: <span class="nv">SEC_TYPE</span><span class="o">=</span><span class="s2">"msdos"</span> <span class="nv">LABEL_FATBOOT</span><span class="o">=</span><span class="s2">"GENTOOLIVE"</span> <span class="nv">LABEL</span><span class="o">=</span><span class="s2">"GENTOOLIVE"</span> <span class="nv">UUID</span><span class="o">=</span><span class="s2">"A168-D76E"</span> <span class="nv">BLOCK_SIZE</span><span class="o">=</span><span class="s2">"512"</span> <span class="nv">TYPE</span><span class="o">=</span><span class="s2">"vfat"</span> <span class="nv">PARTUUID</span><span class="o">=</span><span class="s2">"7437c9e0-02"</span>
/dev/mapper/luksdev: <span class="nv">LABEL</span><span class="o">=</span><span class="s2">"Gentoo"</span> <span class="nv">UUID</span><span class="o">=</span><span class="s2">"a000eea9-d97c-4107-ae39-602049a6acaa"</span> <span class="nv">UUID_SUB</span><span class="o">=</span><span class="s2">"d45b2afd-7250-4ba1-a896-b0e81a20fa4b"</span> <span class="nv">BLOCK_SIZE</span><span class="o">=</span><span class="s2">"4096"</span> <span class="nv">TYPE</span><span class="o">=</span><span class="s2">"btrfs"</span>
</code></pre></div></div>
<p>copy the UUID for the root filesystem upon <strong>luksdev</strong> device, (in the above example is a000eea9-d97c-4107-ae39-602049a6acaa), also copy the UUID for the boot filesystem which resides on /dev/nvme0n1p2 partition (on the above example is 1D97-3854).</p>

<p>This is my fstab</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd /etc <span class="c"># cat fstab</span>
<span class="c"># /etc/fstab: static file system information.</span>
<span class="nv">UUID</span><span class="o">=</span>1D97-3854                                  /boot                           vfat            noatime                                                                         0 1
<span class="nv">UUID</span><span class="o">=</span>a000eea9-d97c-4107-ae39-602049a6acaa       /                               btrfs           noatime,relatime,compress<span class="o">=</span>lzo,ssd,discard<span class="o">=</span>async,subvol<span class="o">=</span>@            0 0
<span class="nv">UUID</span><span class="o">=</span>a000eea9-d97c-4107-ae39-602049a6acaa       /home                           btrfs           noatime,relatime,compress<span class="o">=</span>lzo,ssd,discard<span class="o">=</span>async,subvol<span class="o">=</span>@home        0 0
<span class="nv">UUID</span><span class="o">=</span>a000eea9-d97c-4107-ae39-602049a6acaa       /.snapshots                     btrfs           noatime,relatime,compress<span class="o">=</span>lzo,ssd,discard<span class="o">=</span>async,subvol<span class="o">=</span>@snapshots   0 0
<span class="c"># tmps</span>
tmpfs                                           /tmp                            tmpfs           defaults,size<span class="o">=</span>4G                                                                0 0
tmpfs                                           /run                            tmpfs           <span class="nv">size</span><span class="o">=</span>100M                                                                       0 0
<span class="c"># shm</span>
shm                                             /dev/shm                        tmpfs           nodev,nosuid,noexec                                                             0 0
</code></pre></div></div>
<p><strong>WARNING</strong> I’ve been using the option <strong>discard=async</strong> since I’m using kernel greater then 5.6, if you’re using kernel 5.4.x (or older) don’t use the discard=async option!!!</p>

<h2 id="installing-the-sources">Installing the sources</h2>
<p>Install the kernel, genkernel and cryptsetup:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# emerge <span class="nt">--ask</span> sys-kernel/gentoo-sources
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# emerge <span class="nt">--ask</span> sys-kernel/genkernel
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# emerge <span class="nt">--ask</span> sys-fs/cryptsetup
</code></pre></div></div>
<h3 id="optional---installing-firmware">Optional - Installing firmware</h3>
<p>Some drivers require additional firmware, if you use some of those you need to install the firmware packages:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# emerge <span class="nt">--ask</span> sys-kernel/linux-firmware
</code></pre></div></div>
<h3 id="optional---installing-intel-microcode">Optional - Installing intel microcode</h3>
<p>If you have an Intel cpu and you want to upgrade the microcode you could install the intel-microcode package:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# <span class="nb">mkdir</span> <span class="nt">-p</span> /etc/portage/package.use
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# <span class="nb">echo</span> <span class="s2">"sys-firmware/intel-microcode initramfs"</span> <span class="o">&gt;</span> /etc/portage/package.use/intel-microcode
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# emerge <span class="nt">--ask</span> sys-firmware/intel-microcode
</code></pre></div></div>
<h3 id="configure-genkernelconf">Configure genkernel.conf</h3>
<p>Configure genkernel for systemd, LUKS and Btrfs:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# <span class="nb">cd</span> /etc/
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd /etc <span class="c"># cp -p genkernel.conf genkernel.conf.ORIG</span>
</code></pre></div></div>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd /etc <span class="c"># vim genkernel.conf</span>
...
<span class="nv">MAKEOPTS</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>portageq envvar MAKEOPTS<span class="si">)</span><span class="s2">"</span>
...
<span class="nv">LUKS</span><span class="o">=</span><span class="s2">"yes"</span>
...
<span class="nv">BTRFS</span><span class="o">=</span><span class="s2">"yes"</span>
...
</code></pre></div></div>
<h3 id="run-genkernel">Run genkernel</h3>
<p>Configure your kernel with the preferred options, and then</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# <span class="nb">time </span>genkernel all
</code></pre></div></div>
<h2 id="install-and-configure-grub">Install and configure grub</h2>
<h3 id="install-grub">Install grub</h3>
<p>Configure grub to use efi-64 platform:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# <span class="nb">echo</span> <span class="s1">'GRUB_PLATFORMS="efi-64"'</span> <span class="o">&gt;&gt;</span> /etc/portage/make.conf
</code></pre></div></div>
<p>emerge grub:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# emerge <span class="nt">--ask</span> sys-boot/grub
</code></pre></div></div>
<h3 id="install-grub-1">Install grub</h3>
<p>just run grub-install:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# grub-install <span class="nt">--target</span><span class="o">=</span>x86_64-efi <span class="nt">--efi-directory</span><span class="o">=</span>/boot
</code></pre></div></div>
<h3 id="configure-grub">Configure grub</h3>
<p>First find the LUKS UUID and the root filesystem UUID:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# blkid  | egrep <span class="s1">'(crypto_LUKS|luksdev)'</span>
/dev/nvme0n1p3: <span class="nv">UUID</span><span class="o">=</span><span class="s2">"1ce717f4-5a82-49e7-ae1c-9a92e4c20251"</span> <span class="nv">TYPE</span><span class="o">=</span><span class="s2">"crypto_LUKS"</span> <span class="nv">PARTLABEL</span><span class="o">=</span><span class="s2">"luks"</span> <span class="nv">PARTUUID</span><span class="o">=</span><span class="s2">"c271c93e-6f59-446f-9139-a0b98afab820"</span>
/dev/mapper/luksdev: <span class="nv">LABEL</span><span class="o">=</span><span class="s2">"Gentoo"</span> <span class="nv">UUID</span><span class="o">=</span><span class="s2">"a000eea9-d97c-4107-ae39-602049a6acaa"</span> <span class="nv">UUID_SUB</span><span class="o">=</span><span class="s2">"d45b2afd-7250-4ba1-a896-b0e81a20fa4b"</span> <span class="nv">BLOCK_SIZE</span><span class="o">=</span><span class="s2">"4096"</span> <span class="nv">TYPE</span><span class="o">=</span><span class="s2">"btrfs"</span>
</code></pre></div></div>
<p>In my case the LUKS UUID is 1ce717f4-5a82-49e7-ae1c-9a92e4c20251 and the root UUID is a000eea9-d97c-4107-ae39-602049a6acaa</p>

<p>Edit /etc/default/grub</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# <span class="nb">cd</span> /etc/default/
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd /etc/default <span class="c"># cp -p grub grub.ORIG</span>
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd /etc/default <span class="c"># vim grub</span>
</code></pre></div></div>
<p>and most important configure GRUB_CMDLINE_LINUX with</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">GRUB_CMDLINE_LINUX</span><span class="o">=</span><span class="s2">"init=/usr/lib/systemd/systemd crypt_root=UUID=1ce717f4-5a82-49e7-ae1c-9a92e4c20251 root=UUID=a000eea9-d97c-4107-ae39-602049a6acaa rootflags=subvol=@"</span>
</code></pre></div></div>
<p>where</p>

<table>
  <tbody>
    <tr>
      <td><strong>parameter</strong></td>
      <td><strong>description</strong></td>
    </tr>
    <tr>
      <td>init</td>
      <td>set init to systemd -&gt; /usr/lib/systemd/systemd</td>
    </tr>
    <tr>
      <td>crypt_root</td>
      <td>put here the LUKS UUID (from blkid) of the third partition /dev/nvme0n1p3</td>
    </tr>
    <tr>
      <td>root</td>
      <td>put here the Btrfs UUID (kept from luksdev device on blkid)</td>
    </tr>
    <tr>
      <td>rootflags</td>
      <td>tell the kernel what Btrfs’ subvolume contains root filesystem (@)</td>
    </tr>
  </tbody>
</table>

<p>finally we can run grub-mkconfig:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd /etc/default <span class="c"># cd</span>
<span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# grub-mkconfig <span class="nt">-o</span> /boot/grub/grub.cfg
</code></pre></div></div>
<h2 id="set-the-root-password">Set the root password</h2>
<p>Remember to set the root password:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# passwd
</code></pre></div></div>
<h2 id="rebooting-the-system">Rebooting the system</h2>
<p>Exit the chrooted environment and reboot:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nb">chroot</span><span class="o">)</span> livecd ~# <span class="nb">exit
</span>livecd /mnt/gentoo <span class="c"># shutdown -r now</span>
</code></pre></div></div>

<h2 id="optional-add-swap-subvolume-and-swapfile">Optional: Add swap subvolume and swapfile</h2>
<p>If you want to add a swapfile, inside your Btrfs filesystem, after the first reboot, check at first, the LUKS device mapper created by your initramfs, if you’re using genkernel this device mapper is called <strong>root</strong>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~# blkid | <span class="nb">grep </span>Gentoo
/dev/mapper/root: <span class="nv">LABEL</span><span class="o">=</span><span class="s2">"Gentoo"</span> <span class="nv">UUID</span><span class="o">=</span><span class="s2">"a000eea9-d97c-4107-ae39-602049a6acaa"</span> <span class="nv">UUID_SUB</span><span class="o">=</span><span class="s2">"d45b2afd-7250-4ba1-a896-b0e81a20fa4b"</span> <span class="nv">BLOCK_SIZE</span><span class="o">=</span><span class="s2">"4096"</span> <span class="nv">TYPE</span><span class="o">=</span><span class="s2">"btrfs"</span>
</code></pre></div></div>
<p>now mount it under /mnt/gentoo directory:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~# <span class="nb">mkdir</span> <span class="nt">-p</span> /mnt/gentoo
~# mount /dev/mapper/root /mnt/gentoo
</code></pre></div></div>
<p>create the <strong>@swap</strong> subvolume:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~# btrfs subvolume create /mnt/gentoo/@swap
Create subvolume <span class="s1">'/mnt/gentoo/@swap'</span>
</code></pre></div></div>
<p>create the swapfile, in my case 4G of swapfile, but you can adapt it as your needs:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~# <span class="nb">truncate</span> <span class="nt">-s</span> 0 /mnt/gentoo/@swap/swapfile
</code></pre></div></div>
<p>disable the copy on write and Btrfs compression:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~# chattr +C /mnt/gentoo/@swap/swapfile

~# btrfs property <span class="nb">set</span> /mnt/gentoo/@swap/swapfile compression none
</code></pre></div></div>
<p>and create the 4G swapfile:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~# fallocate <span class="nt">-l</span> 4G /mnt/gentoo/@swap/swapfile

~# <span class="nb">chmod </span>0600 /mnt/gentoo/@swap/swapfile

~# mkswap /mnt/gentoo/@swap/swapfile
Setting up swapspace version 1, size <span class="o">=</span> 4 GiB <span class="o">(</span>4294963200 bytes<span class="o">)</span>
</code></pre></div></div>
<p>create the mountpoint <strong>/.swap</strong>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~# <span class="nb">mkdir</span> /.swap
</code></pre></div></div>
<p>and add two rows in the fstab, one for the <strong>@swap</strong> subvolume, and the second one for the swapfile <strong>/.swap/swapfile</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~# <span class="nb">cd</span> /etc

/etc# <span class="nb">cat </span>fstab
<span class="c"># /etc/fstab: static file system information.</span>
<span class="nv">UUID</span><span class="o">=</span>1D97-3854                                  /boot                           vfat            noatime                                                                         0 1
<span class="nv">UUID</span><span class="o">=</span>a000eea9-d97c-4107-ae39-602049a6acaa       /                               btrfs           noatime,relatime,compress<span class="o">=</span>lzo,ssd,discard<span class="o">=</span>async,subvol<span class="o">=</span>@            0 0
<span class="nv">UUID</span><span class="o">=</span>a000eea9-d97c-4107-ae39-602049a6acaa       /home                           btrfs           noatime,relatime,compress<span class="o">=</span>lzo,ssd,discard<span class="o">=</span>async,subvol<span class="o">=</span>@home        0 0
<span class="nv">UUID</span><span class="o">=</span>a000eea9-d97c-4107-ae39-602049a6acaa       /.snapshots                     btrfs           noatime,relatime,compress<span class="o">=</span>lzo,ssd,discard<span class="o">=</span>async,subvol<span class="o">=</span>@snapshots   0 0
<span class="nv">UUID</span><span class="o">=</span>a000eea9-d97c-4107-ae39-602049a6acaa       /.swap                          btrfs           noatime,relatime,compress<span class="o">=</span>no,ssd,discard<span class="o">=</span>async,subvol<span class="o">=</span>@swap         0 0
/.swap/swapfile                                 none                            swap            sw                                                                              0 0
<span class="c"># tmps</span>
tmpfs                                           /tmp                            tmpfs           defaults,size<span class="o">=</span>4G                                                                0 0
tmpfs                                           /run                            tmpfs           <span class="nv">size</span><span class="o">=</span>100M                                                                       0 0
<span class="c"># shm</span>
shm                                             /dev/shm                        tmpfs           nodev,nosuid,noexec                                                             0 0
</code></pre></div></div>
<p>remember to set <strong>compress=no</strong> to speeding up the swapfile and set <strong>discard=async</strong> only if have a kernel &gt; 5.6.</p>

<p>Final umount /mnt/gentoo and reboot:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~# umount /mnt/gentoo
~# reboot
</code></pre></div></div>]]></content><author><name></name></author><category term="gentoo" /><category term="gentoo" /><category term="luks" /><category term="systemd" /><summary type="html"><![CDATA[Introduction In this post I’ll describe how to install Gentoo with systemd stage3 tarball on UEFI LUKS partition and Btrfs filesystem, using the standard de facto @ subvolume as root file system.]]></summary></entry></feed>