<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mark Zlamal</title>
    <description>The latest articles on DEV Community by Mark Zlamal (@world2mark).</description>
    <link>https://dev.to/world2mark</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F907202%2F635697eb-7d84-4e68-a20e-2e9eb7a45297.png</url>
      <title>DEV Community: Mark Zlamal</title>
      <link>https://dev.to/world2mark</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/world2mark"/>
    <language>en</language>
    <item>
      <title>CockroachDB: live certificate rotation</title>
      <dc:creator>Mark Zlamal</dc:creator>
      <pubDate>Thu, 20 Mar 2025 16:28:23 +0000</pubDate>
      <link>https://dev.to/world2mark/cockroachdb-live-certificate-rotation-1h4e</link>
      <guid>https://dev.to/world2mark/cockroachdb-live-certificate-rotation-1h4e</guid>
      <description>&lt;h2&gt;
  
  
  certificate rotation in a live CRDB environment
&lt;/h2&gt;

&lt;p&gt;On Kubernetes we have a CockroachDB deployment and associated &lt;strong&gt;&lt;em&gt;secret&lt;/em&gt;&lt;/strong&gt; resources that are mapped as volumes in the CRDB pods.  These secrets represent the certificates that are required by the database to operate, and include &lt;strong&gt;CA&lt;/strong&gt; certs, &lt;strong&gt;Node&lt;/strong&gt; certs, and &lt;strong&gt;User&lt;/strong&gt; certs.&lt;/p&gt;

&lt;p&gt;CockroachDB allows you to rotate these certificates in a non-disruptive way that keeps existing client/SQL connections alive, and no rolling restarts are required.&lt;/p&gt;

&lt;p&gt;Because we’re working in a containerized environment, there is a specific sequence of tasks required to accomplish this process.&lt;/p&gt;

&lt;p&gt;This blog covers these basics, and with the help of the linked &lt;em&gt;GitHub&lt;/em&gt; repo you can automate this workflow using the NodeJS app in a reliable, repeatable, reusable way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/world2mark/crdb-cert-rotation" rel="noopener noreferrer"&gt;GitHub: crdb-cert-rotation&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ...to the cert rotation tasks
&lt;/h2&gt;

&lt;p&gt;This section details the sequence of steps that are required to complete the cert-rotation. Like any integrated software system, we will encounter caveats and variances based on target platforms, customizations on the deployments, and security/role restrictions.&lt;/p&gt;

&lt;h3&gt;
  
  
  step 1: update your actual secrets (manual)
&lt;/h3&gt;

&lt;p&gt;This first step requires you to refresh, generate, update your actual certificates.&lt;br&gt;
These can grouped into a single common &lt;strong&gt;&lt;em&gt;secret&lt;/em&gt;&lt;/strong&gt; object or might be broken down into separate secret-objects based on specific usage.  For example the CA can be its own secret, the Node certs might reside in a separate secret, and same goes for user/client certs.&lt;/p&gt;

&lt;p&gt;This is the only step that requires manual intervention.  This is by design because each organization typically has their own workflow to refresh these. Some use Hashicorp Vault, some rely on the &lt;strong&gt;cockroach cert&lt;/strong&gt; commands, while other admins generate their own certs manually.&lt;/p&gt;

&lt;p&gt;These need to be updated in the secrets such that future &lt;strong&gt;pod&lt;/strong&gt; restarts will load these moving forward. &lt;/p&gt;

&lt;h3&gt;
  
  
  step 2: read all the new secrets
&lt;/h3&gt;

&lt;p&gt;This step collects all the secrets tied to CockroachDB. These are specified as a list of tuples that represent the secret name and the mount-path from the CRDB pod perspective.  The example in the GitHub repo shows 2 secrets with mounts, but you might have only 1 secret or possibly more. This tool allows for you to specify any number of mounts and their respective secrets object.&lt;/p&gt;

&lt;h3&gt;
  
  
  step 3: identify the target CRDB pods to refresh (auto)
&lt;/h3&gt;

&lt;p&gt;CockroachDB pods should be labelled using common tags as well as node-specific tags. In the case here, we have a common tag that matches all the pods in the cluster, defined by the environment variable &lt;strong&gt;MZ_CRDB_POD_LABEL_KV&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This tag is actually a tuple (KV), where my example indicates a common tag defined as &lt;strong&gt;&lt;em&gt;zlamal:demo-2025&lt;/em&gt;&lt;/strong&gt; that can be found in all the pods of my cluster.  It’s functionally equal to a label listed as “zlamal: demo-2025”.&lt;/p&gt;

&lt;p&gt;The NodeJS app reads this from the environment (supplied by you in the job-definition YAML), and uses it to capture the running pods so that the tool can perform actions on them in subsequent steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  step 4: we iterate through each pod and perform the certificate updates
&lt;/h3&gt;

&lt;p&gt;This next step is a loop that performs a few sub-steps to accomplish the rotation for each node. These tasks ensure that the pod remains running while bringing the latest secrets, and this can be verified by SHing directly into the pods to inspect the certs folders for changes.&lt;/p&gt;

&lt;h4&gt;
  
  
  step 4.1: delete the old certs
&lt;/h4&gt;

&lt;p&gt;This step deletes the certificates for each secret object.  There can be many secrets, and each secret can contain many certs and keys.&lt;/p&gt;

&lt;h4&gt;
  
  
  step 4.2: save the new certs
&lt;/h4&gt;

&lt;p&gt;This step deletes the certificates for each secret object in an iterative process.  There can be many secrets, and each secret can contain many certs and keys. This is all covered by iterative loops to save all the items.&lt;/p&gt;

&lt;h4&gt;
  
  
  step 4.3: adjust the permissions of the keys
&lt;/h4&gt;

&lt;p&gt;CockroachDB will NOT accept keys that have open permissions. The app sets the read permissions to only the current user, no group access.&lt;/p&gt;

&lt;h4&gt;
  
  
  step 4.4: the magic of sighup
&lt;/h4&gt;

&lt;p&gt;This is the special task that tells CRDB there were changes to the certificates.  The &lt;strong&gt;&lt;em&gt;sighup&lt;/em&gt;&lt;/strong&gt; is an OS-level kill command that doesn’t actually kill the process, it merely tells the process of a HANGUP system event.  CockroachDB knows to reload all certificates in this condition, without dropping any connections.&lt;/p&gt;

&lt;p&gt;You can also see the effects of a SIGHUP in the CockroachDB logging (both on disk and in the console).&lt;/p&gt;

&lt;h2&gt;
  
  
  step 5: ...verify the certificates?
&lt;/h2&gt;

&lt;p&gt;In your CockroachDB admin console, you can find the actual certificates under the &lt;strong&gt;Advanced Debug&lt;/strong&gt; tab, and in the &lt;strong&gt;Even More Advanced Debugging&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;Here you will find “Certificates on this node” that lets you validate the expiry of your certs, and now they should reflect the freshest dates based on step 1 (cert creation saved as secrets).&lt;/p&gt;

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






&lt;h2&gt;
  
  
  Conclusion &amp;amp; References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;All YAML is available in the GitHub repo for this project&lt;/strong&gt; I did not want to mess-up this write up with YAML blocks, so please review them in the repository.&lt;/li&gt;
&lt;li&gt;Have fun, but please work with the Cockroach Enterprise Architects to run through this process for the first time.&lt;/li&gt;
&lt;li&gt;Please review the code. It’s an implementation of these steps, written in JavaScript running under NodeJS&lt;/li&gt;
&lt;li&gt;I have a dockerized image available (currently for ARM64 but can be created for all architectures), but requires a pull-secret.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/world2mark/crdb-cert-rotation" rel="noopener noreferrer"&gt;GitHub: crdb-cert-rotation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openshift</category>
      <category>cockroachdb</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>CockroachDB: fast-start configuration on a fresh cluster</title>
      <dc:creator>Mark Zlamal</dc:creator>
      <pubDate>Tue, 24 Dec 2024 16:58:19 +0000</pubDate>
      <link>https://dev.to/world2mark/cockroachdb-fast-start-configuration-on-a-fresh-cluster-23ha</link>
      <guid>https://dev.to/world2mark/cockroachdb-fast-start-configuration-on-a-fresh-cluster-23ha</guid>
      <description>&lt;p&gt;You frequently deploy CockroachDB clusters, and each time you need to create initial users, initial databases, some pre-loaded tables, adjustments to grants and privileges, and perhaps some custom zone-configurations based on your locality settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pain is repeating this activity over, and over, and over again...
&lt;/h2&gt;

&lt;p&gt;With this guide, after you deploy (or redeploy) your cluster, you can quickly configure it using a repeatable, reliable, and consistent pattern, encapsulated as a single Kubernetes job. This approach eliminates the error-prone and manual process of running your scripts to organize the database.&lt;/p&gt;

&lt;p&gt;This configuration process prepares the database for your workloads, eliminating the need for application-managed configurations. These configs may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;defining database regions&lt;/li&gt;
&lt;li&gt;creation of named databases and schemas&lt;/li&gt;
&lt;li&gt;pre-creating some tables&lt;/li&gt;
&lt;li&gt;applying license keys&lt;/li&gt;
&lt;li&gt;creating initial users or accounts that apps require to operate&lt;/li&gt;
&lt;li&gt;defining permissions, grants, and roles-groups for the users&lt;/li&gt;
&lt;li&gt;backup schedules&lt;/li&gt;
&lt;li&gt;CDC/changefeed jobs&lt;/li&gt;
&lt;li&gt;etc, etc, etc...&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Theory of operation
&lt;/h2&gt;

&lt;p&gt;This process is defined as a Kubernetes Job object that runs once.&lt;/p&gt;

&lt;p&gt;It initially connects using the root-account via certs. These are typically found in the related secrets object that contains the CA-certs, node-certs, and other cluster-applicable certificates.&lt;/p&gt;

&lt;p&gt;Upon connecting to the cluster, the job calls the CockroachDB &lt;em&gt;command line&lt;/em&gt; function &lt;code&gt;__file=&amp;lt;some SQL file&amp;gt;&lt;/code&gt; which points to the mapped/mounted ConfigMap containing the SQL to apply.&lt;/p&gt;

&lt;p&gt;When you create the job spec (code follows later in this blog), it instantly runs, and allows CockroachDB to issue the sequential SQL commands, as-defined in the ConfigMap.  Upon completion, you can check the completed pod and review the console logging to ensure that all your SQL statements were successful.&lt;/p&gt;




&lt;h3&gt;
  
  
  zlamal-initial-sql config map
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Step 1: Define your SQL embedded inside a ConfigMap
&lt;/h4&gt;

&lt;p&gt;The &lt;em&gt;ConfigMap&lt;/em&gt; spec is below.  Note that it's much easier to do this using the OpenShift console UI rather than text-edit the below spec.&lt;/p&gt;

&lt;p&gt;In this example I create an initial user, and an initial table with some inserted dummy-data.  Here is where you can define your database properties such as regions/user-accounts/license-keys/etc to prepare the cluster for client/app connections.&lt;/p&gt;

&lt;p&gt;I wish there was a way to syntax-highlight specific fields that you can focus on. I deliberately use &lt;em&gt;&lt;em&gt;zlamal&lt;/em&gt;&lt;/em&gt; and &lt;em&gt;&lt;em&gt;mz&lt;/em&gt;&lt;/em&gt; (my initials) to help with search/replace when you adopt these specs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-initial-sql&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;zlamal-run-sql-once&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;create user mark with password 'zlamal';&lt;/span&gt;
    &lt;span class="s"&gt;grant admin to mark;&lt;/span&gt;
    &lt;span class="s"&gt;create table aaaa (c0 int, c1 string);&lt;/span&gt;
    &lt;span class="s"&gt;insert into aaaa values (0, 'val0'),(1, 'val1'),(2, 'val2');&lt;/span&gt;
    &lt;span class="s"&gt;-- ...&lt;/span&gt;
    &lt;span class="s"&gt;-- ...&lt;/span&gt;
    &lt;span class="s"&gt;-- ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  zlamal-prep-crdb job
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Step 2: Job object to SQL embedded inside a ConfigMap
&lt;/h4&gt;

&lt;p&gt;The Kubernetes job spec is below.  Again, I wish there was a way to syntax-highlight specific fields that you can focus on, but the idea is that you'll need to adjust the connection-strings, and the names of the K8s resources to align this task to your cluster.&lt;/p&gt;

&lt;p&gt;This &lt;em&gt;job&lt;/em&gt; will mount the &lt;em&gt;ConfigMap&lt;/em&gt; in a folder named "/zlamal-initial-sql". You can change this provided you're consistent with your naming convention.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;batch/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Job&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-prep-crdb&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Never&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-prep-crdb&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb/cockroach:v24.2.5&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/bin/bash&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-ecx'&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
              &lt;span class="s"&gt;exec /cockroach/cockroach&lt;/span&gt;
              &lt;span class="s"&gt;sql&lt;/span&gt;
              &lt;span class="s"&gt;--url&lt;/span&gt;
              &lt;span class="s"&gt;'postgresql://mz-crdb-v11-cockroachdb-public:26257'&lt;/span&gt;
              &lt;span class="s"&gt;--file&lt;/span&gt;
              &lt;span class="s"&gt;/zlamal-initial-sql/zlamal-run-sql-once&lt;/span&gt;
              &lt;span class="s"&gt;--certs-dir=cockroach-certs&lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;client-certs&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/cockroach/cockroach-certs/&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ca&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/cockroach/ca&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-initial-sql-configmap&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/zlamal-initial-sql&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-initial-sql-configmap&lt;/span&gt;
          &lt;span class="na"&gt;configMap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-initial-sql&lt;/span&gt;
            &lt;span class="na"&gt;defaultMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0777&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;client-certs&lt;/span&gt;
          &lt;span class="na"&gt;projected&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mz-crdb-v11-cockroachdb-client-secret&lt;/span&gt;
                    &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ca.crt&lt;/span&gt;
                        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ca.crt&lt;/span&gt;
                      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tls.crt&lt;/span&gt;
                        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;client.root.crt&lt;/span&gt;
                      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tls.key&lt;/span&gt;
                        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;client.root.key&lt;/span&gt;
              &lt;span class="na"&gt;defaultMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;256&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ca&lt;/span&gt;
          &lt;span class="na"&gt;projected&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mz-crdb-v11-cockroachdb-ca-secret&lt;/span&gt;
                  &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ca.key&lt;/span&gt;
                      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ca.key&lt;/span&gt;
            &lt;span class="na"&gt;defaultMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;256&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of-course you'll need to adjust many of the fields in this YAML such as the cockroachDB version, the names, possibly permissions based on the K8s / OpenShift cluster characteristics.&lt;/p&gt;

&lt;p&gt;You should create your own copy of these fragments, and after making the necessary changes, please test them against your environments!&lt;/p&gt;




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

&lt;p&gt;This procedure is a quick reference into Kubernetes Jobs, ConfigMaps, volumes &amp;amp; mounts, and operating CockroachDB by applying some SQL using command-line arguments.&lt;/p&gt;

</description>
      <category>cockroachdb</category>
      <category>openshift</category>
    </item>
    <item>
      <title>CockroachDB on OpenShift: Separate your logs from data!</title>
      <dc:creator>Mark Zlamal</dc:creator>
      <pubDate>Thu, 07 Nov 2024 11:34:39 +0000</pubDate>
      <link>https://dev.to/world2mark/cockroachdb-on-openshift-separate-your-logs-from-data-422b</link>
      <guid>https://dev.to/world2mark/cockroachdb-on-openshift-separate-your-logs-from-data-422b</guid>
      <description>&lt;h2&gt;
  
  
  CockroachDB and persistent volumes
&lt;/h2&gt;

&lt;p&gt;When deployed on Kubernetes or OpenShift, CockroachDB uses persistent volumes (PVs) to store DB data, metadata, state-data, user-data, log files, configuration files.  These volumes are typically file-system mounts that are mapped to disks/SSDs where the data is physically saved in a distributed fashion.  When you operate CockroachDB and run queries, data must be read or written where these operations translate to frequent or continuous disk reads &amp;amp; writes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing the disk: IOPS &amp;amp; throughput
&lt;/h2&gt;

&lt;p&gt;On cloud-managed orchestrators, when you read or write data to disk (PVs), this consumes IOPS and utilizes some of the available IO throughput. These are limiting factors that can result bandwidth saturation, or worse, &lt;strong&gt;throttling&lt;/strong&gt; by the cloud provider under heavier workloads. This condition can be identified by the combination of low CPU usage and high disk latencies, visualized through the CockroachDB UI console hardware dashboard metrics and charts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Divide &amp;amp; conquer
&lt;/h2&gt;

&lt;p&gt;To overcome these limitations, CockroachDB lets you take advantage of multiple, independent PVs to separate the destination of the cockroach runtime data.  CockroachDB &lt;em&gt;&lt;strong&gt;Logging&lt;/strong&gt;&lt;/em&gt; is a good candidate to move out of the critical path by dedicating its own volume/storage. This will help with performance tuning since your SQL/schemas live on their own dedicated volume. In fact it's the &lt;a href="https://www.cockroachlabs.com/docs/stable/recommended-production-settings#storage" rel="noopener noreferrer"&gt;production readiness recommendation&lt;/a&gt; to split the data from the logs into separate PVs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typical CockroachDB deployments
&lt;/h2&gt;

&lt;p&gt;Most CockroachDB clusters implement a single PVC that is assigned to each node in a stateful set. Default configurations in both HELM and Operator managed environments create this 1:1 mapping as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8awd6aahrv3smtzbw3h7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8awd6aahrv3smtzbw3h7.png" alt="Default PV/PVC relationship between nodes and volumes" width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;
Default PV/PVC relationship between nodes and volumes






&lt;h3&gt;
  
  
  Our planned deployment with multiple PVs
&lt;/h3&gt;

&lt;p&gt;By introducing a second PV dedicated for logs, we split the workload and effectively double the IO channels and allows for each to be independently configured. Storage for logs can be significantly reduced when compared to the cockroach-data PV since logs can be rotated/truncated while your business data can grow over time.  This illustration highlights the logical infrastructure layout between nodes and PVs.  &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu878qqtwxfaohmcrrlck.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu878qqtwxfaohmcrrlck.png" alt="Multiple PV/PVCs assigned to each node" width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;
Multiple PV/PVCs assigned to each node






&lt;h2&gt;
  
  
  …to the implementation
&lt;/h2&gt;

&lt;p&gt;We need to make additions to the StatefulSet template along with custom log-configuration settings to direct CockroachDB logs into the new destination PV.&lt;/p&gt;

&lt;h4&gt;
  
  
  The logging “secret” configuration
&lt;/h4&gt;

&lt;p&gt;This resource is the one-stop-shop for all your customized logging properties, including log sinks (output logs to different locations, including over the network), logging channels that are mapped to each sink, the format used by the log messages, any redaction-flags of log messages, the buffering and max sizes of log messages.&lt;/p&gt;

&lt;p&gt;The following log configuration is the smallest/simplest configuration that we will use as a starting point.  Here we keep most defaults, only adjusting the &lt;strong&gt;&lt;em&gt;file-defaults&lt;/em&gt;&lt;/strong&gt; destination path for the actual files, where this path will be mounted to a separate PV defined in the StatefulSet template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;file-defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/cockroach/cockroach-logs&lt;/span&gt;
&lt;span class="na"&gt;sinks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;file-groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;channels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ALL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a comprehensive explanation of this fragments, along with working examples and code-fragments, please refer to the &lt;a href="https://www.cockroachlabs.com/docs/stable/configure-logs" rel="noopener noreferrer"&gt;Cockroach log configuration&lt;/a&gt; documentation so you can tailor the actual logging to your needs.&lt;/p&gt;

&lt;h4&gt;
  
  
  The StatefulSet template configuration
&lt;/h4&gt;

&lt;p&gt;This statefulset fragment only highlights the added template properties to define the PVC and specific mount points to both the log config secret and the new logs folder.  A full, complete StatefulSet example follows this fragment to show the entirety of an actual solution I deployed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StatefulSet&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;volumeClaimTemplates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="c1"&gt;# Fragment 1&lt;/span&gt;
    &lt;span class="c1"&gt;# New volumeClaimTemplate to generate Log PVC &amp;amp; PV&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolumeClaim&lt;/span&gt;
      &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
      &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logsdir&lt;/span&gt;
        &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal&lt;/span&gt;
          &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb&lt;/span&gt;
      &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReadWriteOnce&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10Gi&lt;/span&gt;
        &lt;span class="na"&gt;volumeMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Filesystem&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
          &lt;span class="c1"&gt;# ...&lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# ...&lt;/span&gt;
            &lt;span class="c1"&gt;# ...&lt;/span&gt;
            &lt;span class="c1"&gt;# Fragment 2&lt;/span&gt;
            &lt;span class="c1"&gt;# Additional mount-points for path to logs and log-config&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logsdir&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/cockroach/cockroach-logs/&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;log-config&lt;/span&gt;
              &lt;span class="na"&gt;readOnly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/cockroach/log-config&lt;/span&gt;
          &lt;span class="c1"&gt;# Fragment 3&lt;/span&gt;
          &lt;span class="c1"&gt;# Addition of a new “cockroach start” parameter --log-config-file=...&lt;/span&gt;
          &lt;span class="c1"&gt;# This parameter points CRDB to the mounted log-config secret&lt;/span&gt;
          &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;shell&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-ecx'&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
              &lt;span class="s"&gt;exec /cockroach/cockroach start --log-config-file=/cockroach/log-config/log-config.yaml --join=... --advertise-host=... --certs-dir=/cockroach/cockroach-certs/ --http-port=8081 --port=26257 --cache=11% --max-sql-memory=10%&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadir&lt;/span&gt;
          &lt;span class="na"&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;claimName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadir&lt;/span&gt;
        &lt;span class="c1"&gt;# Fragment 4&lt;/span&gt;
        &lt;span class="c1"&gt;# Establish the logical YAML reference to the logging directory&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logsdir&lt;/span&gt;
          &lt;span class="na"&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;claimName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logsdir&lt;/span&gt;
        &lt;span class="c1"&gt;# Fragment 5&lt;/span&gt;
        &lt;span class="c1"&gt;# Establish logical YAML reference to the log-config secret resource&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;log-config&lt;/span&gt;
          &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-cockroachdb-log-config&lt;/span&gt;
            &lt;span class="na"&gt;defaultMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;420&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Note the “Fragment 1, 2, 3, 4, 5” additions to the StatefulSet 








&lt;p&gt;Here is the complete StatefulSet of these changes,including tags/labels specific to my cluster as a reference example that you can copy and edit to make your own (eg sizes, storage classes, IOPS, tags/labels. etc):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StatefulSet&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-cockroachdb&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/managed-by&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Helm&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb&lt;/span&gt;
    &lt;span class="na"&gt;helm.sh/chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb-14.0.4&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-cockroachdb&lt;/span&gt;
  &lt;span class="na"&gt;volumeClaimTemplates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolumeClaim&lt;/span&gt;
      &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
      &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadir&lt;/span&gt;
        &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal&lt;/span&gt;
          &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb&lt;/span&gt;
      &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReadWriteOnce&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10Gi&lt;/span&gt;
        &lt;span class="na"&gt;volumeMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Filesystem&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolumeClaim&lt;/span&gt;
      &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
      &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logsdir&lt;/span&gt;
        &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal&lt;/span&gt;
          &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb&lt;/span&gt;
      &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReadWriteOnce&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10Gi&lt;/span&gt;
        &lt;span class="na"&gt;volumeMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Filesystem&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb&lt;/span&gt;
        &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal&lt;/span&gt;
        &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&lt;/span&gt;
      &lt;span class="na"&gt;initContainers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
          &lt;span class="na"&gt;terminationMessagePath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dev/termination-log&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;copy-certs&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/bin/sh&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-c'&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cp -f /certs/* /cockroach-certs/; chmod 0400 /cockroach-certs/*.key&lt;/span&gt;
          &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POD_NAMESPACE&lt;/span&gt;
              &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;fieldRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
                  &lt;span class="na"&gt;fieldPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metadata.namespace&lt;/span&gt;
          &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;certs&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/cockroach-certs/&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;certs-secret&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/certs/&lt;/span&gt;
          &lt;span class="na"&gt;terminationMessagePolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;File&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;busybox&lt;/span&gt;
      &lt;span class="na"&gt;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-cockroachdb&lt;/span&gt;
      &lt;span class="na"&gt;schedulerName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default-scheduler&lt;/span&gt;
      &lt;span class="na"&gt;affinity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;podAntiAffinity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;preferredDuringSchedulingIgnoredDuringExecution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
              &lt;span class="na"&gt;podAffinityTerm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;labelSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb&lt;/span&gt;
                    &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal&lt;/span&gt;
                    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb&lt;/span&gt;
                &lt;span class="na"&gt;topologyKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes.io/hostname&lt;/span&gt;
      &lt;span class="na"&gt;terminationGracePeriodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
      &lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
          &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/health?ready=1&lt;/span&gt;
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
              &lt;span class="na"&gt;scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTPS&lt;/span&gt;
            &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
            &lt;span class="na"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
            &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
            &lt;span class="na"&gt;successThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
            &lt;span class="na"&gt;failureThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
          &lt;span class="na"&gt;terminationMessagePath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dev/termination-log&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
          &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/health&lt;/span&gt;
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
              &lt;span class="na"&gt;scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTPS&lt;/span&gt;
            &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
            &lt;span class="na"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
            &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
            &lt;span class="na"&gt;successThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
            &lt;span class="na"&gt;failureThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
          &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;STATEFULSET_NAME&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-cockroachdb&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;STATEFULSET_FQDN&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-cockroachdb.mz-helm-v11.svc.cluster.local&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;COCKROACH_CHANNEL&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes-helm&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grpc&lt;/span&gt;
              &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;26257&lt;/span&gt;
              &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
              &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8081&lt;/span&gt;
              &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
          &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadir&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/cockroach/cockroach-data/&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logsdir&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/cockroach/cockroach-logs/&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;log-config&lt;/span&gt;
              &lt;span class="na"&gt;readOnly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/cockroach/log-config&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;certs&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/cockroach/cockroach-certs/&lt;/span&gt;
          &lt;span class="na"&gt;terminationMessagePolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;File&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cockroachdb/cockroach:v23.2.1'&lt;/span&gt;
          &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;shell&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-ecx'&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
              &lt;span class="s"&gt;exec /cockroach/cockroach start --log-config-file=/cockroach/log-config/log-config.yaml --join=${STATEFULSET_NAME}-0.${STATEFULSET_FQDN}:26257,${STATEFULSET_NAME}-1.${STATEFULSET_FQDN}:26257,${STATEFULSET_NAME}-2.${STATEFULSET_FQDN}:26257 --advertise-host=$(hostname).${STATEFULSET_FQDN} --certs-dir=/cockroach/cockroach-certs/ --http-port=8081 --port=26257 --cache=11% --max-sql-memory=10% &lt;/span&gt;
      &lt;span class="na"&gt;topologySpreadConstraints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;maxSkew&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
          &lt;span class="na"&gt;topologyKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;topology.kubernetes.io/zone&lt;/span&gt;
          &lt;span class="na"&gt;whenUnsatisfiable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ScheduleAnyway&lt;/span&gt;
          &lt;span class="na"&gt;labelSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb&lt;/span&gt;
              &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal&lt;/span&gt;
              &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb&lt;/span&gt;
      &lt;span class="na"&gt;serviceAccount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-cockroachdb&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadir&lt;/span&gt;
          &lt;span class="na"&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;claimName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadir&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logsdir&lt;/span&gt;
          &lt;span class="na"&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;claimName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logsdir&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;log-config&lt;/span&gt;
          &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-cockroachdb-log-config&lt;/span&gt;
            &lt;span class="na"&gt;defaultMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;420&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;certs&lt;/span&gt;
          &lt;span class="na"&gt;emptyDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;certs-secret&lt;/span&gt;
          &lt;span class="na"&gt;projected&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal-cockroachdb-node-secret&lt;/span&gt;
                  &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ca.crt&lt;/span&gt;
                      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ca.crt&lt;/span&gt;
                      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;256&lt;/span&gt;
                    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tls.crt&lt;/span&gt;
                      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node.crt&lt;/span&gt;
                      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;256&lt;/span&gt;
                    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tls.key&lt;/span&gt;
                      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node.key&lt;/span&gt;
                      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;256&lt;/span&gt;
            &lt;span class="na"&gt;defaultMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;420&lt;/span&gt;
      &lt;span class="na"&gt;dnsPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterFirst&lt;/span&gt;
  &lt;span class="na"&gt;podManagementPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Parallel&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;updateStrategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RollingUpdate&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb&lt;/span&gt;
      &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zlamal&lt;/span&gt;
      &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockroachdb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
The logical names/mappings of the volumes are connected together








&lt;h2&gt;
  
  
  Conclusion &amp;amp; References
&lt;/h2&gt;

&lt;p&gt;This is a versatile addition to the standard statefulset because the IOPS can be managed between the PVs, and the plumbing is in-place for log customization. DB admins can easily make changes the to logging channels in a running environment by editing a single log-config file that saved as a secrets object.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cockroachlabs.com/docs/stable/logging-overview" rel="noopener noreferrer"&gt;Cockroach Logging Overview&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.cockroachlabs.com/docs/stable/configure-logs" rel="noopener noreferrer"&gt;Cockroach log configuration&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.cockroachlabs.com/docs/stable/cockroach-start#logging" rel="noopener noreferrer"&gt;Cockroach start: logging&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.cockroachlabs.com/docs/stable/recommended-production-settings" rel="noopener noreferrer"&gt;Production recommendations&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openshift</category>
      <category>cockroachdb</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>CockroachDB: Multi-Region OpenShift using Azure Virtual WAN</title>
      <dc:creator>Mark Zlamal</dc:creator>
      <pubDate>Thu, 10 Aug 2023 16:25:44 +0000</pubDate>
      <link>https://dev.to/world2mark/cockroachdb-multi-region-openshift-using-azure-virtual-wan-5gem</link>
      <guid>https://dev.to/world2mark/cockroachdb-multi-region-openshift-using-azure-virtual-wan-5gem</guid>
      <description>&lt;h3&gt;
  
  
  &lt;em&gt;...art of the practical&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Networking between managed OpenShift clusters has become practical through Azure's &lt;strong&gt;&lt;em&gt;Virtual WAN&lt;/em&gt;&lt;/strong&gt;. When deploying &lt;strong&gt;CockroachDB&lt;/strong&gt; on top, you become the owner of a production-ready, globally available distributed database. Let's create the key components that facilitate secure multi-region networking and architect this database solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  what is this?
&lt;/h3&gt;

&lt;p&gt;This blog features a single logical CockroachDB database that’s deployed on &lt;strong&gt;&lt;em&gt;ARO&lt;/em&gt;&lt;/strong&gt; (Azure Red Hat OpenShift) clusters which are provisioned across multiple regions.&lt;/p&gt;

&lt;p&gt;This article is my second instalment into OpenShift networking at global scale. Here we leverage &lt;strong&gt;Microsoft Azure&lt;/strong&gt;, and this complements my original post &lt;a href="https://dev.to/world2mark/cockroachdb-multi-region-rosa-using-cloud-wan-2h39"&gt;CockroachDB: Multi-Region ROSA using Cloud WAN&lt;/a&gt; that focuses on &lt;strong&gt;AWS&lt;/strong&gt; equivalent infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  the technical catalog
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Virtual Networks&lt;/strong&gt; are the fundamental building blocks for private networks in Azure. This foundation provides the ability for cloud services to interconnect and securely communicate with each other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subnets&lt;/strong&gt; are the specific IP addresses range constructs where resources are grouped and interconnected within a virtual network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ARO&lt;/strong&gt; is the highly available, fully managed service for deploying Red Hat OpenShift instances that are monitored and operated jointly by Microsoft and Red Hat. OpenShift is Kubernetes running on Red Hat Enterprise Linux (RHEL) with built-in management UI services for convenience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Virtual WAN&lt;/strong&gt; is a networking service that brings many networking, security, and globally scaled routing functionalities together, managed by a single operational interface. This is the key offering that lets us manage and connect local and regional &lt;strong&gt;&lt;em&gt;virtual networks&lt;/em&gt;&lt;/strong&gt; together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Virtual HUB&lt;/strong&gt; is the service that lets you connect virtual networks together across sites and regions. The Virtual WAN architecture is a hub and spoke model so at least 1 hub is always required to establish a network-service mesh for your resources across the Microsoft network backbone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CockroachDB&lt;/strong&gt; (aka &lt;em&gt;&lt;strong&gt;CRDB&lt;/strong&gt;&lt;/em&gt;) is the distributed database that is deployed on the worker nodes across the OpenShift clusters.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  the typical ARO cluster
&lt;/h3&gt;

&lt;p&gt;It is important to have a background into the relevant components of a managed Red Hat OpenShift cluster to understand how they can be connected together. When creating an ARO cluster, the resources and services are pre-configured and automatically deployed based on the sizing criteria that is specified during the creation process. Virtual Network &lt;strong&gt;Subnets&lt;/strong&gt; are the required critical networking aspect of ARO clusters that compartmentalize the master and worker node pools. These are used by the Cloud WAN service and Hubs to establish the network routing rules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasn1i22d6ib4ykv53jka.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasn1i22d6ib4ykv53jka.png" alt="ARO Cluster in Virtual Network"&gt;&lt;/a&gt;&lt;/p&gt;
Architecture: This managed OpenShift cluster hosts containerized apps including CockroachDB nodes. Overview of &lt;a href="https://azure.microsoft.com/en-ca/products/openshift" rel="noopener noreferrer"&gt;Microsoft Azure ARO&lt;/a&gt;






&lt;h3&gt;
  
  
  Virtual WAN: facilitator of OpenShift cluster networking
&lt;/h3&gt;

&lt;p&gt;This service defines the networking infrastructure and connectivity rules for the complete solution. The &lt;strong&gt;Virtual WAN&lt;/strong&gt; defines the core network-framework that manages hubs, VPN sites, EspressRoute circuits, and virtual networks. My searches online revealed many great examples of single and multi-hub networks that handle groups of networks, including how to establish links to on-prem environments or other cloud resources (eg: multi cloud solutions).&lt;/p&gt;

&lt;p&gt;In our solution, we will use a single &lt;strong&gt;HUB&lt;/strong&gt; to define our CockroachDB network, and a distinct &lt;strong&gt;Virtual Network&lt;/strong&gt; connections that’s associated with every managed OpenShift cluster in our solution.&lt;/p&gt;

&lt;p&gt;The most important caveat is to define unique subnet ranges for each OpenShift environment, as this is how we’ll differentiate and access the actual network IPs across each node in each cluster.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8x43egsnuzd50wt3ndq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8x43egsnuzd50wt3ndq.png" alt="Multiple ARO Clusters interconnected via Virtual WAN"&gt;&lt;/a&gt;&lt;/p&gt;
Architecture: Virtual WAN allows interconnectivity between ARO clusters across regions, resulting in a network mesh that’s ideal for a distributed CockroachDB platform. Note the unique CIDR ranges for each virtual network.






&lt;h3&gt;
  
  
  the network service mesh
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In the following diagram, all master nodes are hidden for simplicity.  They are typically hosted on the 172.x.0.x/16 subnet, while all workers are hosted on the 172.x.1.x/16 subnet.&lt;/li&gt;
&lt;li&gt;Worker nodes within each cluster are inherently connected; links have been omitted for simplicity in this chart.&lt;/li&gt;
&lt;li&gt;Every node has network visibility to every other node across clusters and regions. This example only illustrates Node 2 links for simplicity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1h29ygstu13je1wtpfyc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1h29ygstu13je1wtpfyc.png" alt="Network service mesh"&gt;&lt;/a&gt;&lt;/p&gt;
Architecture: The CockroachDB network mesh requires that every worker node hosting a CockroachDB pod has routing/visibility to every other worker node (by extension every other CockroachDB pod) in all the OpenShift clusters across regions.






&lt;h3&gt;
  
  
  let’s create the Azure resources
&lt;/h3&gt;

&lt;p&gt;The following steps will establish the necessary cluster infrastructure based on the included screenshots, but your settings may vary, such as IP CIDR ranges or regional settings.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Create your Virtual Networks
&lt;/h4&gt;

&lt;p&gt;For each OpenShift cluster, you need a corresponding &lt;strong&gt;virtual network&lt;/strong&gt; with 2 subnets.  One subnet is for OpenShift workers, the other is for OpenShift masters. This is required for associating an ARO cluster with a valid virtual network during the creation process.&lt;br&gt;
Below is my new virtual network, named &lt;strong&gt;zlamal-aro-canada-01&lt;/strong&gt; with the 2 subnets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0tbl50x9f2uxzfxy5vv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0tbl50x9f2uxzfxy5vv.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
I use default as my masters subnet, and create the second workers subnet with a uniquely routable CIDR range.



&lt;p&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 2: Create your OpenShift Clusters
&lt;/h4&gt;

&lt;p&gt;We will focus on the networking aspects of the Azure ARO &lt;em&gt;Creation Wizard&lt;/em&gt;.&lt;br&gt;
After specifying the cluster name, region and node sizing, the &lt;strong&gt;Virtual Networks&lt;/strong&gt; list will be filtered to the same region of the cluster region.  In my example I chose Canada Central for my Cluster &amp;amp; networking. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F877s7utumgfmb4wd9kfr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F877s7utumgfmb4wd9kfr.png" alt="Image description"&gt;&lt;/a&gt;Master subnet is my default virtual network subnet, while the Worker subnet is tied to my worker-virtual-network subnet.&lt;/p&gt;

&lt;p&gt;I’ve chosen to make the endpoints &lt;strong&gt;visible&lt;/strong&gt; for convenience and to avoid the challenges of establishing a VPN framework for this exercise.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Notice: This screenshot indicates &lt;strong&gt;172.18.x.x&lt;/strong&gt; virtual network subnets while the prior screenshot shows &lt;strong&gt;172.23.x.x&lt;/strong&gt;. This is because during this blog write-up I’ve created yet another virtual network for an upcoming OpenShift cluster, but normally these CIDR ranges would align with each other.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Step 3: Create a Virtual WAN instance
&lt;/h4&gt;

&lt;p&gt;In this diagram, the first step is to specify the management region of the Cloud WAN instance.  Actual networking at this phase is agnostic of locality.  Specify your name and ensure the &lt;strong&gt;Type&lt;/strong&gt; is &lt;strong&gt;Standard&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 4: Create the Hub instance
&lt;/h4&gt;

&lt;p&gt;This hub resides in the Virtual WAN, and will connect all the OpenShift clusters together in the subsequent step. The hub is also assigned a region, but ultimately accepts connections from any Azure network-resource provisioned in any region.&lt;/p&gt;

&lt;p&gt;In this example it’s named &lt;em&gt;&lt;strong&gt;canada-east-hub&lt;/strong&gt;&lt;/em&gt;, along with a special-defined address space used internally by Microsoft to manage/operate the hub instance.  I’ve chosen a non-overlapping CIDR of &lt;strong&gt;172.100.0.0/16&lt;/strong&gt; and this will not interfere with my clusters or any future additions to this network. To save on costs I kept the hub capacity at the minimal possible throughput and connection capability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj2t0qrvspo1vw2iqa2p3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj2t0qrvspo1vw2iqa2p3.png" alt="Image description"&gt;&lt;/a&gt;”Basics” tab is the only one that’s relevant.  All others can be kept as default.&lt;/p&gt;

&lt;p&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 5: Add the connections to the Hub
&lt;/h4&gt;

&lt;p&gt;Every OpenShift cluster resides on a unique Virtual Network. This step establishes the connectivity on all the virtual networks. The hub manages and operates this pool of connections.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34lqpwmt4b5k7jvegcz8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34lqpwmt4b5k7jvegcz8.png" alt="Image description"&gt;&lt;/a&gt;To add a connection, select your Virtual WAN, under &lt;em&gt;Virtual network connections&lt;/em&gt; click on &lt;strong&gt;Add connection&lt;/strong&gt;. From here you can label this interface, apply the Hub, apply default settings, and complete the peering.&lt;/p&gt;

&lt;p&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As you scale-out, create, or delete OpenShift clusters, here is where the interfaces are established or removed.  This means creation of a Virtual WAN along with the Hub is a one-time process.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgm3f8950wxgwfez2acye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgm3f8950wxgwfez2acye.png" alt="Image description"&gt;&lt;/a&gt;Upon completion of virtual network associations with the hub, the network topology will be updated with your complete network.&lt;/p&gt;

&lt;p&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 6: Establish CockroachDB nodes
&lt;/h4&gt;

&lt;p&gt;Every OpenShift is interconnected at this point. This means that if you open a terminal window to any running pod, you can &lt;strong&gt;curl&lt;/strong&gt; to any worker on any of the clusters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;From this point you can create a unique OpenShift deployment instance for each worker-node affinity to ensure a 1:1 relationship between workers and CRDB nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Catalog the IP addresses of the worker nodes in your &lt;em&gt;&lt;strong&gt;primary&lt;/strong&gt;&lt;/em&gt; OpenShift cluster that will serve as the starting point for CockroachDB.  Once this primary cluster of 3+ nodes is initialized, all the other nodes can use these 3+ IP addresses, along with corresponding service &lt;strong&gt;NodePorts&lt;/strong&gt; to connect to every other node on the cluster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Load balancers can be created per-OpenShift-cluster, providing additional redundancy if a cluster becomes unavailable.  This also clearly defines multiple access-points for clients to use and prioritize based on latency and geographic location.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This YAML fragment illustrates the locality and network parameters for a single node in the cluster. The &lt;em&gt;&lt;strong&gt;--join&lt;/strong&gt;&lt;/em&gt; statement is typically static for all nodes, while the sql-addr, listen-addr, and advertise-addr will be unique for each node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          ...
          ...
apiVersion: apps/v1
kind: Deployment
metadata:
  name: crdb-deployment-zlamal-172-23-1-4
  labels:
    crdb-cluster: crdb-zlamal
spec:
  selector:
    matchLabels:
      crdb-pod-name: crdb-pod-zlamal-172-23-1-4
  replicas: 1
  template:
    metadata:
      labels:
        crdb-pod-name: crdb-pod-zlamal-172-23-1-4
        crdb-cluster: crdb-zlamal
    ...
    spec:
      ...
      containers:
          command:
            - /bin/bash
            - '-ecx'
            - &amp;gt;-
              exec /cockroach/cockroach
              start
              --cache=.25
              --logtostderr
              --certs-dir=/cockroach/cockroach-certs
              --locality=country=CA,region=CA-East,zone=Zone-1
              --sql-addr=:26257
              --listen-addr=:26357
              --advertise-addr=172.23.1.4:31951
              --join 172.23.1.6:31950,172.23.1.4:31951,172.23.1.5:31952
              ...
              ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each CRDB node will require its own &lt;em&gt;&lt;strong&gt;NodePort&lt;/strong&gt;&lt;/em&gt; service to establish the internal database network mesh, while a common service can internally load-balance connections for co-located applications.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This example service aligns with the above fragment to define unique NodePort port numbers that must not overlap with other services in the current OpenShift cluster.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kind: Service
apiVersion: v1
metadata:
  name: crdb-service-zlamal-172-23-1-4
spec:
  ports:
    - name: sql-access
      protocol: TCP
      port: 26257
      targetPort: 26257
      nodePort: 31851
    - name: node-comms
      protocol: TCP
      port: 26357
      targetPort: 26357
      nodePort: 31951
    - name: console-ui
      protocol: TCP
      port: 8080
      targetPort: 8080
  type: NodePort
  selector:
    crdb-pod-name: crdb-pod-zlamal-172-23-1-4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;A generic service would have the &lt;em&gt;&lt;strong&gt;selector&lt;/strong&gt;&lt;/em&gt; not to a pod, but rather the common label of the deployments. In this case set the selector to &lt;strong&gt;crdb-zlamal&lt;/strong&gt; for the load balancers to properly interface with all the active pods.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example a load balancer service definition handing the local cluster will have a selector as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Service
metadata:
  name: crdb-zlamal-load-balancer
  labels:
    crdb-cluster: crdb-zlamal
spec:
  ports:
    - name: console-ui
      port: 8080
      protocol: TCP
      targetPort: 8080
    - name: sql-access
      port: 26257
      protocol: TCP
      targetPort: 26257
  type: LoadBalancer
  selector:
    crdb-cluster: crdb-zlamal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  conclusion
&lt;/h3&gt;

&lt;p&gt;These Azure network services abstract away the complexities of infrastructure, security, and resource-management, offering a view of the entire architecture from a single pane of glass.  I can't cover every detail into these technologies, but Azure also provides service &amp;amp; network monitoring, security controls, and visual representations of the connected ecosystem.&lt;/p&gt;

&lt;p&gt;For a starter-pack of Kubernetes templates to help with deploying CockroachDB, go to the &lt;a href="https://github.com/cockroachdb/cockroach/tree/master/cloud/kubernetes" rel="noopener noreferrer"&gt;Cockroach/cloud/kubernetes&lt;/a&gt; GitHub repo.&lt;/p&gt;




&lt;h3&gt;
  
  
  references
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.cockroachlabs.com" rel="noopener noreferrer"&gt;Cockroach  Labs&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/world2mark/cockroachdb-multi-region-rosa-using-cloud-wan-2h39"&gt;CockroachDB: Multi-Region ROSA (AWS) using Cloud WAN&lt;/a&gt;&lt;br&gt;
&lt;a href="https://learn.microsoft.com/en-us/azure/virtual-wan/virtual-wan-about" rel="noopener noreferrer"&gt;What is Azure Virtual WAN?&lt;/a&gt;&lt;br&gt;
&lt;a href="https://azure.microsoft.com/en-ca/products/openshift" rel="noopener noreferrer"&gt;Azure ARO&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cockroachdb</category>
      <category>azure</category>
      <category>openshift</category>
    </item>
    <item>
      <title>CockroachDB: row-level TTL to simulate Redis</title>
      <dc:creator>Mark Zlamal</dc:creator>
      <pubDate>Mon, 03 Apr 2023 16:53:52 +0000</pubDate>
      <link>https://dev.to/world2mark/cockroachdb-row-level-ttl-to-simulate-redis-4a12</link>
      <guid>https://dev.to/world2mark/cockroachdb-row-level-ttl-to-simulate-redis-4a12</guid>
      <description>&lt;h2&gt;
  
  
  what this blog covers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;row-level TTL implementation in CockroachDB&lt;/li&gt;
&lt;li&gt;examples of &lt;em&gt;&lt;strong&gt;prepared statements&lt;/strong&gt;&lt;/em&gt; with execution parameters&lt;/li&gt;
&lt;li&gt;usage demo that illustrate automatic deletion &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  what this is not...
&lt;/h2&gt;

&lt;p&gt;Redis is a feature-rich, in-memory key/value database designed for high-performance caching and text-based queries against key-strings.  This blog is not meant to replace a true Redis use-case, instead it provides an implementation to the most frequently used Redis capabilities, namely &lt;strong&gt;GET&lt;/strong&gt;, &lt;strong&gt;SET&lt;/strong&gt;, and &lt;strong&gt;EXPIRE&lt;/strong&gt; functions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no expressions or conditional get capabilities&lt;/li&gt;
&lt;li&gt;no gets using multiple keys, scans, or wildcard queries&lt;/li&gt;
&lt;li&gt;no in-memory stores. Data is distributed across nodes, but always using disk for I/O. &lt;em&gt;There are in-memory options that would provide benefit to this solution, but not covered here. At the bottom of this blog is a link to in-memory stores to improve performance.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  implementation: table definition
&lt;/h3&gt;

&lt;p&gt;The Redis table must contain 3 specific columns to facilitate the capabilities tied to row-level TTL, namely the  &lt;em&gt;&lt;strong&gt;key&lt;/strong&gt;&lt;/em&gt;, the &lt;em&gt;&lt;strong&gt;value&lt;/strong&gt;&lt;/em&gt;, and the &lt;em&gt;&lt;strong&gt;expired_at&lt;/strong&gt;&lt;/em&gt; columns. The names of these columns can be adjusted to meet your application needs, provided that the prepared statements below are in-sync with your naming conventions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;redis_tbl&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;expired_at&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ttl_expiration_expression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'expired_at'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;redis_tbl&lt;/span&gt; &lt;span class="n"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;gc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ttlseconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You can introduce additional app-specific columns including indexes to accommodate your workload.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;key&lt;/strong&gt; and &lt;strong&gt;value&lt;/strong&gt; data-types can also be tailored to meet your needs.  &lt;em&gt;In fact I often use &lt;strong&gt;JSONB&lt;/strong&gt; data-type for values for easy data-processing in my NodeJS apps.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;expired_at&lt;/strong&gt; column is a timestamp in &lt;strong&gt;seconds&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Note on the  &lt;em&gt;&lt;strong&gt;gc.ttlseconds&lt;/strong&gt;&lt;/em&gt; alteration.  The default CockroachDB garbage collector removes tombstones after 25 hours (90000s) so the recommended practice is to protect your storage capacity by reducing this window, especially under workloads with many short-lived or churning keys.&lt;/li&gt;
&lt;li&gt;This example has GC set to 300 seconds (5 min), but should be adjusted based on anticipated usage and can be revisited &amp;amp; altered in a production environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  implementation: prepared statements
&lt;/h3&gt;

&lt;p&gt;For convenience, we create 3 prepared statements to provide the core functionality tied to &lt;strong&gt;set&lt;/strong&gt;, &lt;strong&gt;get&lt;/strong&gt;, and &lt;strong&gt;expire&lt;/strong&gt; capabilities. These can be tailored to meet your application needs, including data-type augmentation or additional parameters.&lt;/p&gt;

&lt;blockquote&gt;

&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;prepare&lt;/span&gt; &lt;span class="n"&gt;redis_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;
  &lt;span class="n"&gt;upsert&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="n"&gt;redis_tbl&lt;/span&gt; &lt;span class="k"&gt;values&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;em&gt;The &lt;strong&gt;redis_set&lt;/strong&gt; statement saves key/value data including an expiry duration (in seconds).&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;

&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;prepare&lt;/span&gt; &lt;span class="n"&gt;redis_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;
  &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;redis_tbl&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;expired_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;em&gt;The &lt;strong&gt;redis_get&lt;/strong&gt; statement retrieves the stored value that’s identified by the key.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;

&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;prepare&lt;/span&gt; &lt;span class="n"&gt;redis_expire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;
  &lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;redis_tbl&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;expired_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;em&gt;The &lt;strong&gt;redis_expire&lt;/strong&gt; statement updates the expiry duration of an existing key to this new value (in seconds).&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  testing &amp;amp; usage: set, get, expire
&lt;/h3&gt;

&lt;p&gt;Below is some basic usage of these operations.  Note that time is of the essence when running tests since this intended to be a real-time demo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;execute&lt;/span&gt; &lt;span class="n"&gt;redis_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mz1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'hello1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;-- entry is saved with a 10 second TTL&lt;/span&gt;

&lt;span class="k"&gt;execute&lt;/span&gt; &lt;span class="n"&gt;redis_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mz1'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;-- returns the 'mz1/hello1' row;&lt;/span&gt;

&lt;span class="k"&gt;execute&lt;/span&gt; &lt;span class="n"&gt;redis_expire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mz1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;-- entry is updated with a fresh 10 second TTL window&lt;/span&gt;

&lt;span class="k"&gt;execute&lt;/span&gt; &lt;span class="n"&gt;redis_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mz1'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;-- returns the 'mz1/hello1' row;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;...wait 11 seconds to observe the DB changes (auto-deleted/expired rows)...&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;execute&lt;/span&gt; &lt;span class="n"&gt;redis_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mz1'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;-- returns 0 rows;&lt;/span&gt;

&lt;span class="k"&gt;execute&lt;/span&gt; &lt;span class="n"&gt;redis_expire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mz1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;-- this is a no-op since mz1 expired due to row-level TTL.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test-harness is not exhaustive but demonstrates the core behavior of CockroachDB highlighting outputs when keys exist and what you can expect after they’ve expired.&lt;/p&gt;




&lt;h2&gt;
  
  
  conclusion
&lt;/h2&gt;

&lt;p&gt;If you’re already operating on a CockroachDB database, this is a quick extension to simulate Redis-style capabilities without the need to provision a dedicated Redis platform. For example, during the development of a web-application that requires &lt;strong&gt;&lt;em&gt;session &amp;amp; cookie&lt;/em&gt;&lt;/strong&gt; tracking, this technique  is a quick add-on that lets you prove out your code and demo the app. When you’re ready to produce a production environment, you can then provision a true Redis platform and use that to perform the full scale of capabilities.&lt;/p&gt;




&lt;h2&gt;
  
  
  terminology &amp;amp; resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.cockroachlabs.com/docs/stable/row-level-ttl.html"&gt;Batch Delete Expired Data with Row-Level TTL&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cockroachlabs.com/docs/stable/timestamp.html"&gt;TIMESTAMP / TIMESTAMPTZ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cockroachlabs.com/docs/stable/cockroach-start.html#store"&gt;CockroachDB in-memory storage options&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://redis.io"&gt;redis.io homepage&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cockroachdb</category>
      <category>redis</category>
    </item>
    <item>
      <title>CockroachDB: trace logging with Datadog</title>
      <dc:creator>Mark Zlamal</dc:creator>
      <pubDate>Wed, 11 Jan 2023 13:47:49 +0000</pubDate>
      <link>https://dev.to/world2mark/cockroachdb-trace-logging-with-datadog-1cm1</link>
      <guid>https://dev.to/world2mark/cockroachdb-trace-logging-with-datadog-1cm1</guid>
      <description>&lt;h2&gt;
  
  
  trace logs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Trace logs&lt;/em&gt;&lt;/strong&gt; are the detailed internal synchronous &amp;amp; asynchronous operations that take place to fulfill a complete transaction or deliver a result.&lt;/p&gt;

&lt;h2&gt;
  
  
  CockroachDB trace logs
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;&lt;em&gt;CockroachDB&lt;/em&gt;&lt;/strong&gt; these operations may include any combination of network interactions, calls to the &lt;a href="https://www.cockroachlabs.com/docs/stable/architecture/storage-layer.html" rel="noopener noreferrer"&gt;storage layer&lt;/a&gt;, &lt;a href="https://www.cockroachlabs.com/docs/stable/architecture/sql-layer.html" rel="noopener noreferrer"&gt;SQL layer&lt;/a&gt;, &lt;a href="https://www.cockroachlabs.com/docs/stable/architecture/distribution-layer.html" rel="noopener noreferrer"&gt;distribution layer&lt;/a&gt;, &lt;a href="https://www.cockroachlabs.com/docs/stable/architecture/life-of-a-distributed-transaction.html#txncoordsender" rel="noopener noreferrer"&gt;query batching&lt;/a&gt;, &lt;a href="https://www.cockroachlabs.com/docs/stable/architecture/reads-and-writes-overview.html" rel="noopener noreferrer"&gt;reads/updates/writes&lt;/a&gt;, and various runtime coordinator services.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Normally this is the level of detail is what you’d find inside a CockroachDB &lt;a href="https://www.cockroachlabs.com/docs/stable/cockroach-debug-zip.html" rel="noopener noreferrer"&gt;debug ZIP bundle&lt;/a&gt;, and specifically the &lt;strong&gt;&lt;em&gt;jaeger-file&lt;/em&gt;&lt;/strong&gt; that lets you visualize and interact with the execution flow to identify where processing/waiting time is spent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  improving database performance
&lt;/h2&gt;

&lt;p&gt;Datadog provides tools to assist in the tasks of database tuning, query-tuning, and improvements to schemas.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What’s special is that trace-logs are sent to &lt;a href="https://www.datadoghq.com/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt; in real-time during query execution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the Datadog portal you can monitor, filter, and explore the same jaeger-style activity on these dissected operations to help guide you with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;narrowing down bottle-necks in the cluster network topology&lt;/li&gt;
&lt;li&gt;capturing transient issues tied to network fluctuations&lt;/li&gt;
&lt;li&gt;isolating root-causes of poor-performing queries&lt;/li&gt;
&lt;li&gt;observing application behaviour that send queries&lt;/li&gt;
&lt;li&gt;tracking down the impact of sub-optimal schemas&lt;/li&gt;
&lt;li&gt;providing additional context for Cockroach Labs support teams&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  to the architecture
&lt;/h2&gt;

&lt;p&gt;The trace logging system is divided into 3 areas of integration.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The CockroachDB cluster (source; host machine)&lt;/li&gt;
&lt;li&gt;The Datadog agent (OTLP protocol; host machine)&lt;/li&gt;
&lt;li&gt;The Datadog web portal (sink; in cloud)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxgcd4djtsqzpnu5wb09.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxgcd4djtsqzpnu5wb09.png" alt="Core Architecture" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;
architecture of CockroachDB and Datadog integration




&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  1) in the cloud: Datadog
&lt;/h4&gt;

&lt;p&gt;Datadog is an integrated cloud-portal offering data-aggregation services, including all the tools needed to specify data-sources, infrastructure observability, build interactive dashboards, explore metrics, define alerts, monitors, and so on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.datadoghq.com/developers/marketplace/" rel="noopener noreferrer"&gt;CockroachDB data source integration (Datadog marketplace)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CockroachDB is one of the many data-source integrations that’s available through their vendor marketplace. This service establishes the core settings for a typical CRDB installation that are required to capture metrics, text-log data, and defines all the back-end monitored properties.&lt;/p&gt;

&lt;h4&gt;
  
  
  2) on the host machine: Datadog agent
&lt;/h4&gt;

&lt;p&gt;Here we have the Datadog agent. It’s a binary executable that runs as a service in the background, listening to &lt;a href="https://opentelemetry.io/docs/concepts/what-is-opentelemetry" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; data sources.  You can find installation instructions on the main portal with download links specific to your target OS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.datadoghq.com/agent/" rel="noopener noreferrer"&gt;Datadog agents&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They provide agents for many environments, including cloud-based and operator-driven deployments for managed Kubernetes and OpenShift.&lt;/p&gt;

&lt;h4&gt;
  
  
  3) on the host machine: CockroachDB
&lt;/h4&gt;

&lt;p&gt;CockroachDB relies on the OpenTelemetry protocol as part of the observability instrumentation.  this can be enabled by applying the cluster setting property &lt;strong&gt;trace.opentelemetry.collector&lt;/strong&gt; to associate with the Datadog agent.&lt;/p&gt;

&lt;p&gt;In this post the agent running locally on my laptop along with CockroachDB so I’m sticking to the default Datadog OLTP workload port for the telemetry connection.&lt;/p&gt;




&lt;h2&gt;
  
  
  to the installation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  create an API key
&lt;/h3&gt;

&lt;p&gt;Datadog provides a multi-tenant view to user-content and data-sources. API keys are used to identify and differentiate who’s data we’re working with, and where the data comes from.&lt;/p&gt;

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



&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  install the agent
&lt;/h3&gt;

&lt;p&gt;Browse to the &lt;a href="https://app.datadoghq.com/account/settings#agent" rel="noopener noreferrer"&gt;Agents Page&lt;/a&gt; and download the binary suitable for your OS or platform.&lt;/p&gt;

&lt;p&gt;Once installed, configuration files made available so that you can associate your CockroachDB installation with the agent, and associate the agent with your Datadog account.&lt;/p&gt;

&lt;h4&gt;
  
  
  associate the Agent with Datadog
&lt;/h4&gt;

&lt;p&gt;Using the API key generated above, you must edit the &lt;strong&gt;datadog.yaml&lt;/strong&gt; file and save the key. The OTLP listening port is 4317 by default and can also be changed if you have multiple listeners or agents present.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;edit &lt;strong&gt;~/.datadog-agent/datadog-agent/datadog.yaml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
...
api_key: **4fxxxxxxxxxxxxxxxxxxxxxxxxxxxxe0**
...
...
otlp_config:
  receiver:
    protocols:
      grpc:
        endpoint: localhost:4317
...
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  associate CockroachDB with the agent
&lt;/h4&gt;

&lt;p&gt;Next step (optional): Tag your services and specify CockroachDB endpoints that match your database installation.&lt;/p&gt;

&lt;p&gt;Tagging is important where multiple Cockroach DB instances are running on the same server and you need to differentiate between them on your dashboards.&lt;/p&gt;

&lt;p&gt;The default prometheus URL setting is &lt;strong&gt;&lt;em&gt;localhost:8080/_status/vars&lt;/em&gt;&lt;/strong&gt; but can be changed as follows:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;edit &lt;strong&gt;~/.datadog-agent/conf.d/cockroachdb.d/conf.yaml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
...
## Every instance is scheduled independent of the others.
#
instances:
  - openmetrics_endpoint: http://localhost:8091/_status/vars
    tags:
      - service:mzmz-crdb-8091
      - env:mzmz-env-8091
    service: mzmz-crdb-8091
    ...
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CockroachDB cluster settings
&lt;/h3&gt;

&lt;p&gt;The last setting is to establish the link between CockroachDB and the Datadog agent. This is accomplished by setting the trace-collector to the running Datadog agent as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@localhost:26257/defaultdb&amp;gt; set cluster setting trace.opentelemetry.collector='localhost:4317';
SET CLUSTER SETTING
Time: 78ms total (execution 78ms / network 0ms)

root@localhost:26257/defaultdb&amp;gt; set cluster setting sql.trace.log_statement_execute=on;
SET CLUSTER SETTING
Time: 41ms total (execution 41ms / network 0ms)

root@localhost:26257/defaultdb&amp;gt; set tracing=on;
SET TRACING
Time: 0ms total (execution 0ms / network 0ms)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Reference
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://www.cockroachlabs.com/docs/stable/set-vars.html#set-tracing" rel="noopener noreferrer"&gt;CockroachLabs Docs: Set Tracing&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SET TRACING changes the trace recording state of the current session. A trace recording can be inspected with the SHOW TRACE FOR SESSION statement.
SET TRACING=off;  -- Trace recording is disabled.
SET TRACING=cluster;  -- Trace recording is enabled; distributed traces are collected.
SET TRACING=on;  -- Same as cluster.
SET TRACING=kv;  -- Same as cluster except that "kv messages" are collected instead of regular trace messages.
SET TRACING=results; -- Result rows and row counts are copied to the session trace. This must be specified in order for the output of a query to be printed in the session trace.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  testing the environment
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Launch CockroachDB&lt;/li&gt;
&lt;li&gt;Open the Datadog Agent local admin console (default address is &lt;a href="http://127.0.0.1:5002" rel="noopener noreferrer"&gt;http://127.0.0.1:5002&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Checks&lt;/strong&gt; --&amp;gt; &lt;strong&gt;Checks Summary&lt;/strong&gt; you should see all green checkmarks.

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2x02qydio2grub2kz439.png" alt="Checks Summary for Datadog Agent" width="800" height="435"&gt;In my installation, I have 2 CockroachDB instances configured, but only one is running.


&lt;a href=""&gt; &lt;/a&gt;
&lt;a href=""&gt; &lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sign into your Datadog HQ account&lt;/li&gt;
&lt;li&gt;Left hand navigation bar: APM --&amp;gt; Traces

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fknyreix1bly472iaqcib.png" alt="APM Traces" width="800" height="617"&gt;Tracing from CockroachDB emits large volumes of data. Searching for &lt;strong&gt;sql txn&lt;/strong&gt; will remove the noise and focus the trace-spans onto your queries for inspection.


&lt;a href=""&gt;&lt;/a&gt;
&lt;a href=""&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Upon running a query, the full trace and spans are sent to this UI and can be dissected.

&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faob02o8cltb5d6nev8k3.png" alt="APM Traces" width="800" height="473"&gt;Visualize where (and how) time is spent processing a query, and can be used as a baseline for comparison against related queries. A typical example is to confirm range hot-spots due to long wait-times acquiring a write-latch.


&lt;a href=""&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The UI lets you explore the query history and with capabilities such as sorting by &lt;strong&gt;duration&lt;/strong&gt;, isolating long-running statements is a quick process.&lt;/p&gt;

&lt;p&gt;By adjusting the filters, you can switch to other CockroachDB run-time activities such as liveness inspections, tracking jobs, or garbage-collector event logs.&lt;/p&gt;




&lt;h2&gt;
  
  
  conclusion
&lt;/h2&gt;

&lt;p&gt;Tracing capabilities provided by Datadog offers a non-invasive solution for processing and visualizing logs across many deployment options. It's a fully-managed &amp;amp; integrated cloud solution that offers all the back-end instrumentation allowing you to focus on the health of your applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  terminology &amp;amp; resources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;APM&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Application Performance Monitoring&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;OTLP&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The OpenTelemetry Protocol specification describes the encoding/transport/delivery mechanism of telemetry data between sources/collectors/backends. &lt;a href="https://opentelemetry.io/docs/concepts/what-is-opentelemetry" rel="noopener noreferrer"&gt;What is OpenTelemetry?&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Datadog &amp;amp; CockroachDB integration&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Datadog Agent ingests metrics and traces in the OpenTelemetry format (OTLP), which can be produced by OpenTelemetry-instrumented applications such as CockroachDB.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;configure CRDB&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.cockroachlabs.com/docs/stable/datadog.html" rel="noopener noreferrer"&gt;CockroachDB cluster configuration&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;configure Datadog&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.datadoghq.com/agent/guide/agent-configuration-files/?tab=agentv6v7" rel="noopener noreferrer"&gt;Client-side Datadog configuration&lt;/a&gt; (client-side Datadog configuration)&lt;br&gt;
&lt;a href="https://docs.datadoghq.com/integrations/" rel="noopener noreferrer"&gt;Cloud account Datadog integration&lt;/a&gt; (cloud account Datadog integration)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;trace&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Information about the sub-operations performed as part of a high-level operation (a query or a transaction). This information is internally represented as a tree of "spans", with a special "root span" representing a whole SQL transaction&lt;br&gt;
&lt;a href="https://www.cockroachlabs.com/docs/stable/set-vars.html#set-tracing" rel="noopener noreferrer"&gt;CockroachLabs Docs: Set Tracing&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>emptystring</category>
    </item>
    <item>
      <title>CockroachDB: Multi-Region ROSA using Cloud WAN</title>
      <dc:creator>Mark Zlamal</dc:creator>
      <pubDate>Fri, 14 Oct 2022 18:48:19 +0000</pubDate>
      <link>https://dev.to/world2mark/cockroachdb-multi-region-rosa-using-cloud-wan-2h39</link>
      <guid>https://dev.to/world2mark/cockroachdb-multi-region-rosa-using-cloud-wan-2h39</guid>
      <description>&lt;h2&gt;
  
  
  what is this?
&lt;/h2&gt;

&lt;p&gt;This blog dives into a distributed CockroachDB solution that is hosted on a fully-managed OpenShift platform known as ROSA.&lt;/p&gt;

&lt;p&gt;For each region, a ROSA environment is provisioned, and are connected together using the AWS Cloud WAN (aka SD-WAN, Software-defined WAN).&lt;/p&gt;

&lt;p&gt;To support multiple regions in a cloud ecosystem, the solution is tightly coupled across &lt;em&gt;IaaS&lt;/em&gt;, &lt;em&gt;PaaS&lt;/em&gt;, and &lt;em&gt;SaaS&lt;/em&gt;. &lt;/p&gt;




&lt;h3&gt;
  
  
  IaaS, PaaS, and SaaS
&lt;/h3&gt;

&lt;p&gt;There is a natural harmony and integration across these layers. This blog will highlight the capabilities, the value-add, and the required work-effort when building enterprise-ready production environments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS Cloud (&lt;em&gt;IaaS&lt;/em&gt;):&lt;/strong&gt; AWS is the cloud vendor that hosts our global environments, facilitating all the infrastructure and platform services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ROSA (&lt;em&gt;PaaS&lt;/em&gt;):&lt;/strong&gt; This is the Red Hat managed OpenShift platform that sits on the AWS ecosystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CockroachDB (&lt;em&gt;SaaS&lt;/em&gt;):&lt;/strong&gt; This is the distributed database that's deployed across multiple OpenShift (ROSA) clusters on AWS.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  background: CockroachDB deployment options
&lt;/h3&gt;

&lt;p&gt;Cockroach Labs offers CockroachDB as a &lt;strong&gt;self hosted&lt;/strong&gt; solution, and &lt;em&gt;as-a-service&lt;/em&gt; &lt;strong&gt;dedicated&lt;/strong&gt; solution (including &lt;em&gt;serverless&lt;/em&gt;), each with specific benefits tied to the desired use-cases &amp;amp; requirements.&lt;/p&gt;

&lt;h4&gt;
  
  
  self hosted
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;This relies on the expertise of customers to stand-up and operate the entire ecosystem. This means specialists making decisions about infrastructure and specialists who provision and connect it all together. So while you retain complete control across all layers, it’s no surprise that this approach is highly involved and complex from design to creation to maintenance of the entire stack.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  dedicated and as-a-service offerings
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;This turnkey option lets customers focus on their data and apps, offering many great advantages that promote fast-to-market strategies with an evolving set of capabilities including hybrid connectivity.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  where does ROSA fit in the CockroachDB landscape?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3q2zardro8r8gkdj2zu0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3q2zardro8r8gkdj2zu0.png" alt="ROSA is the bridge between self-hosted and dedicated offerings"&gt;&lt;/a&gt;&lt;/p&gt;
...the bridge between &lt;b&gt;self-hosted&lt;/b&gt; and 
 &lt;b&gt;dedicated&lt;/b&gt; offerings.



&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ROSA is a balanced sweet-spot that offers the flexibility of &lt;strong&gt;self hosted&lt;/strong&gt; while automating everything else. It serves as best of both worlds by abstracting-away the annoyances of infrastructure decisions and countless choices on service types. This is all done through a prescriptive creation process using internal terraform scripts that are highly optimized for the cloud of choice (AWS in this case). This results in a rapid provisioned, ready-to-use, globally available Kubernetes platform.&lt;/p&gt;

&lt;p&gt;This middle-ground simplifies day-2 operations so you remain focused on the databases, applications, and business logic, while inheriting all the AWS resources to explore this environment.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Like any containerization environment, ROSA lets you push applications, services, and software such as CockroachDB on the platform. The key advantage that specifically benefits CockroachDB is the ease in scaling the system. By scaling I mean true resizing of the cloud environment ranging from physical or virtual hardware all the way to the workloads themselves.  This is possible because ROSA being a cloud-native project, is tightly integrated with AWS. You can start small and operate a cost-effective database solution. Should there be demand for more storage or performance, this end-to-end scaling can be accomplished in short order.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  ...to the AWS environment
&lt;/h3&gt;

&lt;p&gt;When provisioning a ROSA Kubernetes cluster, something very exciting happens...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ivtjgn3ntb32p5brt3c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ivtjgn3ntb32p5brt3c.png" alt="Detailed ROSA Architecture"&gt;&lt;/a&gt;&lt;/p&gt;
Architecture: In less than 1 hour, ROSA becomes a complete, ready-to-use, scalable, highly-available solution ready for containerized apps such as CockroachDB. &lt;a href="https://console.redhat.com/openshift" rel="noopener noreferrer"&gt;Red Hat OpenShift provisioning console&lt;/a&gt;



&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Through built-in terraform services, over 2 dozen services and resources are provisioned within AWS. Everything is secured, pre-configured and inter-connected including worker nodes, infrastructure nodes, master nodes. These server nodes are all accessible through load-balancers and network routing tables, gateways are established and IP addresses are defined, and finally access-controls are mapped out through defined security groups. The best part? It’s all laid out in the AWS cloud console, providing full visibility and control over this entire ecosystem.&lt;/p&gt;

&lt;h4&gt;
  
  
  ...so what does this mean for CockroachDB?
&lt;/h4&gt;

&lt;p&gt;The good news is that we already provide extensive documentation and guides on Kubernetes and OpenShift deployment using Operators, Helm charts, including collections of YAML specs and fragments.  This makes ROSA a sweet-spot for rapid standing-up of a CockroachDB database in the cloud.&lt;br&gt;
&lt;a href="https://www.cockroachlabs.com/docs/v22.1/deploy-cockroachdb-with-kubernetes-openshift.html" rel="noopener noreferrer"&gt;Deploy CockroachDB on Red Hat OpenShift&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.cockroachlabs.com/docs/v22.1/orchestrate-cockroachdb-with-kubernetes-multi-cluster.html?filters=eks" rel="noopener noreferrer"&gt;Orchestrate CockroachDB Across Multiple Kubernetes Clusters&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/cockroachdb/cockroach/tree/master/cloud/kubernetes" rel="noopener noreferrer"&gt;YAML Fragments on GitHub&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.cockroachlabs.com/docs/stable/scale-cockroachdb-kubernetes.html" rel="noopener noreferrer"&gt;Scaling using operators, manual configs, helm charts&lt;/a&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  ...to the NETWORKING!
&lt;/h3&gt;

&lt;p&gt;Networking challenges for multi-region deployments aren’t specific to ROSA, it’s a general challenge in cloud-computing under any environment. I chose ROSA because it’s the path of least resistance, full featured, highly modernized, easy to deploy, scalable, everything I’ve already mentioned.  The clusters sit on VPCs. VPCs are hosted in single regions, and regions belong to the global AWS Cloud ecosystem, and this is a common theme in all cases.&lt;/p&gt;
&lt;h4&gt;
  
  
  legacy approaches: Transit Gateways &amp;amp; VPC Peering
&lt;/h4&gt;

&lt;p&gt;In the following diagrams you’ll see the use of transit gateways or VPC peering to establish connectivity, which is now considered a legacy approach. These examples highlight the growing challenges, complexity, and risk as a cluster extends across regions.&lt;/p&gt;
&lt;h4&gt;
  
  
  legacy: networking across 2 regions
&lt;/h4&gt;

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

&lt;blockquote&gt;
&lt;p&gt;This diagram represents the simplest solution where &lt;b&gt;transit gateways&lt;/b&gt; or &lt;b&gt;VPC peering&lt;/b&gt; is used to establish connectivity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  legacy: networking across 3 regions
&lt;/h4&gt;

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

&lt;blockquote&gt;
&lt;p&gt;When a 3&lt;sup&gt;rd&lt;/sup&gt; region is added, each routing table must be updated to reflect the new IP range and destination.  Challenges around networking continue since all regions need explicit tables defined to see the other regions.  It’s a game of continuous maintenance of routing tables across all the clusters, either via VPC peering or Transit-gateways.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  legacy: networking across 4 regions
&lt;/h4&gt;

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

&lt;blockquote&gt;
&lt;p&gt;By adding a 4&lt;sup&gt;th&lt;/sup&gt; region you quickly see the complexity growing (almost exponential) since each cluster must have explicit access to the others. Every peering connection governs their IP range, this range points to the Transit gateway that in-turn connects to a network of other transit gateways distributed across regions.  Everything must be perfectly defined and becomes a tedious &amp;amp; highly error-prone process since a single typo could take-out the entire ecosystem. To make matters worse, the addition of this new cluster requires you to visit all operational/production/live clusters and update those routing tables with this new VPC connection.  Even in the AWS portal, navigating across the resources to find every field becomes a complicated and unmanageable mess that I can’t even fully depict here.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  AWS Recommendation: &lt;b&gt;Cloud WAN&lt;/b&gt;
&lt;/h2&gt;

&lt;p&gt;I had a meeting with AWS staff, and they recommended the use of the Cloud WAN services instead of transit gateways or VPC peering.  Transit Gateways are still the fundamental building block for communication between VPCs, in fact the underlying physical AWS WAN architecture continues to use transit gateways, but with added intelligence and UI tools to make it very easy to use and economical as you expand.&lt;/p&gt;

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

&lt;p&gt;The same solution as above, using the AWS Software-Defined WAN. Immediately you see that it’s a flat network connecting all the clusters using the global AWS backbone. This picture shows the ENTIRETY of the implementation and network configuration. You only need a single route that points to a core network, and the SD-WAN manages all the connectivity across VPCs, across edges, regions, and partitions.  That’s it. The AWS management console is a single pane-of-glass graphical UI providing visibility across the entire AWS network.&lt;/p&gt;

&lt;p&gt;The best part is that when you add a new Cluster, you don’t have to make any changes to the existing clusters or infrastructure.  The only rule that applies to all implementations: Each VPC must have a unique IP Address range for it to be propagated across the global network.&lt;/p&gt;
&lt;h4&gt;
  
  
  option 1: public Postgres endpoints
&lt;/h4&gt;

&lt;p&gt;Here is an end-product for a traditional 3-region CockroachDB cluster, allowing users to pick &amp;amp; choose, or be assigned to the closest CockroachDB edge-location with lowest latency. All inter-node communication is hidden behind the SD-WAN, while direct access to the database ports is handled by regional load balancers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubfquqizh1grr8pwvvmj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubfquqizh1grr8pwvvmj.png" alt="CockroachDB with Public Load Balancers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  option 2: private Postgres endpoints, public-facing colocated apps
&lt;/h4&gt;

&lt;p&gt;For customers who do not want to expose actual CockroachDB Postgres connection-interfaces, instead they publicize application endpoints that can be firewalled and protected. These visible connections point to APIs, UIs, mobile apps services, while the data-processors and apps are all behind the firewall, sitting on the same subnet at the multi-region CockroachDB instance. VPC architectures like this are often referred to as secure landing zones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cbe0x64qjm9m73gepc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cbe0x64qjm9m73gepc2.png" alt="CockroachDB in a private environment"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Secure Landing Zones
&lt;/h4&gt;

&lt;p&gt;These secure landing zones are entire platforms that operate under the umbrella of AWS compliance controls, security, encryption. You inherit all the governance rules for networking, user-permissions, and app permissions to protect these workloads. Data-hungry apps such as analytics/OLAP or CDC workloads are all prime candidates as colocated apps in these environments.  I mentioned these workloads are on the same subnet, effectively all services are zero network-hops away from CockroachDB. Data can be consumed, perhaps using follower reads or pinned ranges to the physical nodes in the subnet. You’re literally benefiting from the performance of a local area network with no ingress or egress charges, limitations, or packet loss due to unreliable networks. At the risk of being controversial, we can enhance CockroachDB performance by running the entire cluster using insecure mode because nothing is accessible outside the secure landing zones.&lt;/p&gt;


&lt;h3&gt;
  
  
  connect our VPCs together: Cloud WAN on AWS
&lt;/h3&gt;

&lt;p&gt;The first step is to create a global network. This defines your environment that will manage all the connections, policies, routes, and data metrics.&lt;/p&gt;

&lt;p&gt;In my project I created a "core network", selecting 2 regions that will be connected (us-east-2, us-west-2). You can always add and remove regions across the entire AWS ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flt3u5k8qo8atprw4e137.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flt3u5k8qo8atprw4e137.png" alt="AWS network backbone: us-west-2 to us-east-2"&gt;&lt;/a&gt;&lt;/p&gt;
AWS network backbone: us-west-2 to us-east-2



&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  my Cloud WAN policy
&lt;/h4&gt;

&lt;p&gt;The next step is to establish a network policy against this new segment. This policy governs the rights and extended capabilities for the connections, regions, rights, routes, and conditions for a connection. I left most of this as default, with an example policy JSON that works for me below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "version": "2021.12",
  "core-network-configuration": {
    "vpn-ecmp-support": true,
    "asn-ranges": [
      "64512-65534"
    ],
    "edge-locations": [
      {
        "location": "us-east-2"
      },
      {
        "location": "us-west-2"
      }
    ]
  },
  "segments": [
    {
      "name": "PrimarySegment",
      "edge-locations": [
        "us-east-2",
        "us-west-2"
      ],
      "require-attachment-acceptance": false
    }
  ],
  "attachment-policies": [
    {
      "rule-number": 100,
      "condition-logic": "and",
      "conditions": [
        {
          "type": "any"
        }
      ],
      "action": {
        "association-method": "constant",
        "segment": "PrimarySegment"
      }
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  establish Cloud WAN connections
&lt;/h4&gt;

&lt;p&gt;This final step is where the actual interfacing to the VPCs takes place (and you'll see that VPN, Transit Gateways, and other site-to-site options are provided).&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;By selecting a region where your VPC is provisioned (along with ROSA on that VPC), you pick the VPC and &lt;strong&gt;private&lt;/strong&gt; subnet that ROSA provisioned. It's the private subnet that all the workers and servers are attached to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  back at the regional VPCs...
&lt;/h4&gt;

&lt;p&gt;Find your ROSA VPC (region-specific), and go into the private subnet. This subnet has a link to the &lt;strong&gt;Route Table&lt;/strong&gt; that's associated with this subnet. Here is where you establish the network route between this subnet and global network.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgm9oegku2hm39i2tnafl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgm9oegku2hm39i2tnafl.png" alt="Subnet routing tables"&gt;&lt;/a&gt;&lt;/p&gt;
All traffic in the range 10.x.x.x will enter SD-WAN, except for the &lt;b&gt;local&lt;/b&gt; traffic.



&lt;blockquote&gt;
&lt;p&gt;This process needs to be done for each ROSA VPC in every region. Note that all ROSA VPCs must have unique CIDR ranges. In my demo I defined 3 ROSA clusters with &lt;strong&gt;local&lt;/strong&gt; CIDR ranges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10.100.0.0 (cluster 1, above example screenshot)&lt;/li&gt;
&lt;li&gt;10.110.0.0 (cluster 2)&lt;/li&gt;
&lt;li&gt;10.120.0.0 (cluster 3)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  back at the Cloud WAN portal...
&lt;/h4&gt;

&lt;p&gt;You can now verify the routes across each edge location to validate that the network propagation is complete and active. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygd6wo7s8ycw45fgr7g0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygd6wo7s8ycw45fgr7g0.png" alt="Routes with all the CIDR destinations"&gt;&lt;/a&gt;&lt;/p&gt;
Routes with all the CIDR destinations.



&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The topology trees and maps can be explored for a graphical representation of your networks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk6xirfclhw664mgqagju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk6xirfclhw664mgqagju.png" alt="Network topology tree down to the VPCs"&gt;&lt;/a&gt;&lt;/p&gt;
Network topology tree down to the VPCs



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjl7hsclh6z4bf8daxekv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjl7hsclh6z4bf8daxekv.png" alt="Network topology map showing the connections"&gt;&lt;/a&gt;&lt;/p&gt;
Network topology map showing the connections.



&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  test connectivity between regions
&lt;/h4&gt;

&lt;p&gt;The key to success is to ensure that every worker node in every ROSA cluster can talk to each other.&lt;br&gt;
I create a dummy pod &lt;strong&gt;on each cluster/each region&lt;/strong&gt; just to get access to a terminal window so I can run &lt;strong&gt;curl&lt;/strong&gt; commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: dummy-curl-pod
spec:
  containers:
    - name: dummy-curl-pod
      image: curlimages/curl
      command: [ "sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          printenv MY_NODE_NAME MY_HOST_IP MY_POD_NAME;
          sleep 20;
        done;
      env:
        - name: MY_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: MY_HOST_IP
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Issue a curl command to a compute node in a different cluster&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/ $ curl 10.120.3.5
curl: (7) Failed to connect to 10.120.3.5 port 80 after 0 ms: Connection refused
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;success!&lt;/strong&gt; This error message tells us that while there are no services listening to port 80 on that node, the server is &lt;strong&gt;&lt;em&gt;reachable&lt;/em&gt;&lt;/strong&gt;!&lt;br&gt;
This one-time test needs to be run at all edges/clusters to ensure that CockroachDB nodes can properly communicate (to at least a single node). This ensures database integrity and data replication across the entire platform.&lt;br&gt;
&lt;strong&gt;&lt;em&gt;Delete these pods after testing is done&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  from the perspective of CockroachDB...
&lt;/h3&gt;

&lt;p&gt;This blog does not dive into the installation process, but it's a typical Kubernetes-focused methodology of creating deployments, services, routes, etc (see &lt;strong&gt;&lt;em&gt;caveats&lt;/em&gt;&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;When CockroachDB is live, the admin console provides visibility to the entire cluster (all regions), along with latency-charts and maps of node-deployments to monitor the overall health of the database.&lt;/p&gt;

&lt;p&gt;In this example, we have 3 ROSA clusters (each hosting 3 worker nodes with CockroachDB installed), totalling 9 worker nodes.  One ROSA cluster is on the west coast (AWS Oregon), and the other two ROSA clusters are in OHIO.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fys6jofsoiozpmgpx6fdd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fys6jofsoiozpmgpx6fdd.png" alt="Map of the ROSA-deployed CockroachDB service across 2 regions"&gt;&lt;/a&gt;Map of the ROSA-deployed CockroachDB service across 2 regions&lt;/p&gt;




&lt;p&gt;Investigating the latency, there is approximately 52ms between the regional edges, while ROSA clusters in the same data-center have sub-ms latency.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxdalfcreif1rkit26a4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxdalfcreif1rkit26a4.png" alt="Latency chart across all 9 worker nodes"&gt;&lt;/a&gt;Latency chart across all 9 worker nodes.&lt;/p&gt;




&lt;h3&gt;
  
  
  conclusion
&lt;/h3&gt;

&lt;p&gt;I can't cover every detail into the architecture, design, and deployment since any of these topics and sub-topics can be a discipline on its own and ideally delivered in an in-person working session with Q&amp;amp;A and conversations rather than a blog.&lt;br&gt;
This solution is merely a proof of concept that allows me to leverage Red Hat and AWS resources to the maximum extent in a reliable and repeatable environment.&lt;/p&gt;




&lt;h3&gt;
  
  
  caveats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes services, deployments, persistent volume claims, secrets, and load balancers need to be used across each ROSA cluster. These are found on &lt;a href="https://github.com/cockroachdb/cockroach/tree/master/cloud/kubernetes" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.cockroachlabs.com/docs/v22.1/cockroach-start" rel="noopener noreferrer"&gt;cockroach start join&lt;/a&gt; syntax must specify proper server (worker-node) IPs that are part of the ROSA clusters.&lt;/li&gt;
&lt;li&gt;CockroachDB certificates must be created for secure environments.&lt;/li&gt;
&lt;li&gt;ROSA EC2 instances (eg: worker-nodes) are inaccessible by default, governed by the Access-Control-List &lt;strong&gt;inbound rules&lt;/strong&gt; and &lt;strong&gt;outbound rules&lt;/strong&gt;.  You will need to adjust them to allow traffic from the other CIDR ranges.  For convenience I've been known to allow 0/0, all-ports, both ways in VPCs since the IP ranges are virtual and inaccessible.  &lt;strong&gt;I would love to know what your thoughts and concerns are on this.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  references
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://console.redhat.com/openshift/" rel="noopener noreferrer"&gt;Red Hat OpenShift provisioning console&lt;/a&gt;&lt;br&gt;
&lt;a href="https://aws.amazon.com/blogs/networking-and-content-delivery/aws-cloud-wan-and-aws-transit-gateway-migration-and-interoperability-patterns" rel="noopener noreferrer"&gt;Migration to SD-WAN from TGW&lt;/a&gt;&lt;br&gt;
&lt;a href="https://aws.amazon.com/cloud-wan" rel="noopener noreferrer"&gt;Cloud Wan Product Overview&lt;/a&gt;&lt;br&gt;
&lt;a href="https://ngoyal16.medium.com/vpc-peering-or-transit-gateway-b0f1176874f" rel="noopener noreferrer"&gt;VPC Peering vs Transit Gateways&lt;/a&gt;&lt;br&gt;
&lt;a href="https://cloud.redhat.com/blog/using-the-multus-cni-in-openshift" rel="noopener noreferrer"&gt;MULTUS CNI in OpenShift&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/k8snetworkplumbingwg/multus-cni/blob/master/docs/quickstart.md" rel="noopener noreferrer"&gt;MULTUS CNI GitHub Quickstart&lt;/a&gt;&lt;br&gt;
&lt;a href="https://catalog.redhat.com/software/operators/detail/5ec54aa3535cb70ab8c02996" rel="noopener noreferrer"&gt;Red Hat Advanced Cluster Management for Kubernetes&lt;/a&gt;&lt;br&gt;
&lt;a href="https://aws.amazon.com/blogs/containers/red-hat-openshift-service-on-aws-architecture-and-networking" rel="noopener noreferrer"&gt;AWS Architecture on ROSA (MZRs)&lt;/a&gt;&lt;br&gt;
&lt;a href="https://thechief.io/c/editorial/7-advantages-openshift-over-kubernetes" rel="noopener noreferrer"&gt;7 Advantages Of OpenShift Over Kubernetes&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/vpc/latest/cloudwan/cloudwan.pdf" rel="noopener noreferrer"&gt;For the tech-savvy and initiated (Cloud WAN pdf)&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.globenewswire.com/news-release/2021/02/18/2178094/0/en/Global-Application-Container-Market-2021-to-2026-Growth-Trends-COVID-19-Impact-and-Forecasts.html" rel="noopener noreferrer"&gt;Cloud App Trends)&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.enterprisestorageforum.com/software/containerization-market" rel="noopener noreferrer"&gt;Containerization Market&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.enterprisestorageforum.com/software/containerization-trends" rel="noopener noreferrer"&gt;Containerization Trends&lt;/a&gt;&lt;br&gt;
&lt;a href="https://enterprisersproject.com/article/2022/1/5-kubernetes-trends-watch-2022" rel="noopener noreferrer"&gt;Growth in managed services: ROSA&lt;/a&gt;&lt;br&gt;
&lt;a href="https://connect.redhat.com/blog/forrester-red-hat-partner-openshift-tei-study" rel="noopener noreferrer"&gt;Forrester: Red Hat partnerships for OpenShift&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cockroachdb</category>
      <category>aws</category>
      <category>openshift</category>
    </item>
    <item>
      <title>Docker Solution: CockroachDB with Grafana Logging &amp; Monitoring</title>
      <dc:creator>Mark Zlamal</dc:creator>
      <pubDate>Thu, 18 Aug 2022 00:51:00 +0000</pubDate>
      <link>https://dev.to/world2mark/docker-solution-cockroachdb-with-grafana-logging-monitoring-27dd</link>
      <guid>https://dev.to/world2mark/docker-solution-cockroachdb-with-grafana-logging-monitoring-27dd</guid>
      <description>&lt;h2&gt;
  
  
  what is this?
&lt;/h2&gt;

&lt;p&gt;A prescriptive approach to deploying CockroachDB with integrated logging, monitoring, and alerting through Grafana.&lt;/p&gt;

&lt;h2&gt;
  
  
  what's special here?
&lt;/h2&gt;

&lt;p&gt;This blog provides an overview of the containerized database environment that leverages key services such as &lt;em&gt;Prometheus&lt;/em&gt;, &lt;em&gt;Fluentd&lt;/em&gt;, &lt;em&gt;Loki&lt;/em&gt;, and a handful of supporting components bundled into a &lt;u&gt;single package&lt;/u&gt;. This package is hosted on GitHub, pre-configured using typical settings and can be easily adjusted to match your environment.&lt;/p&gt;

&lt;p&gt;

&lt;strong&gt;&lt;a href="https://github.com/cockroachlabs/Docker-CRDB" rel="noopener noreferrer"&gt;github.com/cockroachlabs/Docker-CRDB&lt;/a&gt;&lt;/strong&gt;

&lt;/p&gt;

&lt;p&gt;This &lt;strong&gt;Docker-CRDB&lt;/strong&gt; GitHub repository provides the core project files while this article highlights the components and how the overall solution fits together. &lt;/p&gt;

&lt;h2&gt;
  
  
  your takeaways?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Coverage of the components and technologies used in this CockroachDB / Logging solution.&lt;/li&gt;
&lt;li&gt;Overview of the architecture, how everything is tied together within Docker. No need to be a subject matter expert in these areas.&lt;/li&gt;
&lt;li&gt;A pre-built reference solution that you can download and make your own.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhjmzmidg27rpqfk42sj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhjmzmidg27rpqfk42sj.png" alt="Logging components used here"&gt;&lt;/a&gt;&lt;/p&gt;
Technologies and components leveraged in this blog



&lt;h3&gt;
  
  
  logging is complicated
&lt;/h3&gt;

&lt;p&gt;It's complicated because there are many ways to architect a solution, many ways to deploy the solution. It can be containerized and orchestrated through Kubernetes. It can be deployed as your own self-managed cloud solution, or you can leverage Grafana’s paid subscription for their integrated cloud-managed services.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;em&gt;good news&lt;/em&gt;&lt;/strong&gt; is that there is extensive documentation into logging, configuration, workshops, technologies, approaches, repositories, fragments, tips, and tricks.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;em&gt;bad news&lt;/em&gt;&lt;/strong&gt; is that there is extensive documentation into logging, configuration, workshops, technologies, approaches, repositories, fragments, tips, and tricks.&lt;/p&gt;

&lt;p&gt;In my journey I wasn't looking for deep-dives into these topics, I just want a reference solution that's already pre-built, pre-configured, operational with minimal amount of integration steps.&lt;/p&gt;

&lt;p&gt;At the same time I don't want automation through ansible or terraform since they can hide key integration aspects, potentially taking away from my understanding of how everything is tied together.&lt;/p&gt;

&lt;p&gt;This project is deployed and operated as a set of independent and interacting Docker containers, where each container runs a single image that manages a single task. No orchestration through Kubernetes, and everything runs locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  ...to the architecture
&lt;/h2&gt;

&lt;p&gt;This solution is divided into 2 perspectives using a Docker bridge network.&lt;/p&gt;

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

&lt;a href="https://github.com/cockroachlabs/Docker-CRDB/blob/main/images/architecture-3-Node.png" rel="noopener noreferrer"&gt;Core architecture (GitHub)&lt;/a&gt;




&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The top half (public network) represents access to pre-configured endpoints that the host machine can connect to to interact with the platform.  Typical interactions can be browsers, potential workload-apps, CockroachDB clients, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The lower half (private network) is the isolated and containerized 'sandbox' of apps and services that interconnect using virtualized network ports and hosts within the framework of the Docker and the Docker bridge network.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  the public network
&lt;/h3&gt;

&lt;p&gt;The upper half is the public network where we can interact with the database and all available logging services. This is effectively the host machine and all accessible endpoints to workloads outside of Docker. All the ports shown here (eg: 3000, 9090, 8080, 26257, …) are defined in the settings as defaults in this project, and must be available host-ports when this project is deployed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you have any conflicts, say another unrelated project/app/workload uses one or more of these ports, then you can adjust the port number through the provided configuration and docker-compose files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  the private network
&lt;/h3&gt;

&lt;p&gt;The lower half is the private network, under the umbrella of Docker running on the host. You can see all the pieces, connected together via networked-interaction-chain that process (source/sink) logging data that’s eventually consumed by the user.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Port conflicts typically do not occur in the private network because each container is treated as a virtualized host within Docker. The key-example here is the set of 3 fluentd instances that listen to port 5170, but because each is treated as a unique host (and hostname), you can distinguish between them and connect accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each orange box is a container running a single image of the highlighted component, and these are all running inside a bridge network. This bridge is a private Docker network, conceptually similar to a virtual-private-cloud. Every running container is treated as a virtual host and treated as a distinct service with ports that can be exposed across the bridge network.  Each container (eg: host) has visibility to all other containers within this network but only to exposed ports defined by the docker-compose configuration file.  Outside of this private-network, none of these exposed ports are visible or accessible to the host machine running Docker.&lt;/p&gt;

&lt;h2&gt;
  
  
  data and log flow
&lt;/h2&gt;

&lt;p&gt;Starting at the right-hand side, we have a containerized 3-node CockroachDB cluster. Normally the database sends logs directly into the cockroach-data/logs folder, but here it’s configured to use fluentd as our log sink, and this is the first step in the chain.&lt;/p&gt;

&lt;h3&gt;
  
  
  so what’s Fluentd?
&lt;/h3&gt;

&lt;p&gt;It’s an open source data collector. It unifies log collection and consumption in a formatted, tagged, buffered, consistent way across all your applications. Fluentd can then save this structured data back into the filesystem, or as the basis of this project, Fluentd sends the formatted data via local-networking to Loki, and this is the next piece of the puzzle.&lt;/p&gt;

&lt;h3&gt;
  
  
  ...and Loki?
&lt;/h3&gt;

&lt;p&gt;Loki is a scalable, multi-tenant logs aggregator and time-series database. It’s similar to Prometheus but specifically designed for TEXT-based analytics, indexing, searching, scanning, and querying facilitation inside Grafana.&lt;/p&gt;

&lt;h3&gt;
  
  
  what about Prometheus?
&lt;/h3&gt;

&lt;p&gt;In Parallel to the above log flows, there is another network link from CockroachDB directly to the Prometheus container. This connection facilitates the operational metrics that allow us to perform queries on Cockroach statistics, usage, SQL, data volumes, contention rates, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  destination: Grafana!
&lt;/h3&gt;

&lt;p&gt;Finally in Grafana we defined data-sources that listen to both Loki and Prometheus, and this is the final sink to our logs.&lt;/p&gt;

&lt;p&gt;As mentioned earlier we have a handful of endpoints exposed to the public network, notably from Cockroach and Grafana so we can access their fancy UI and run logging queries against the CockroachDB nodes.&lt;/p&gt;

&lt;h2&gt;
  
  
  alerting
&lt;/h2&gt;

&lt;p&gt;The alerting framework is separated from the core architecture because it's an optional capability and requires API/Service keys from 3rd party cloud services. In our example, when a trigger in Grafana is activated, it calls a NodeJS endpoint that issues an API call to Twilio and SendGrid. These services send live email and global SMS messages to a recipient, notifying them of this alert with context.&lt;/p&gt;

&lt;p&gt;In Grafana, the alert is configured to monitor prometheus metrics. When the threshold value is reached, Grafana triggers this alert and calls web-hook that sends a JSON payload containing the properties of the alert, and custom fields such as API key information and recipient details.&lt;/p&gt;

&lt;p&gt;The NodeJS application that listens to this alerting endpoint (webhook URL &amp;amp; Port) formats the data into a human readable format, leveraging email-templates, SMS formatting, and sends the new payload to Twilio through their API services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzu41vjen5drgxfcz7tu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzu41vjen5drgxfcz7tu.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;a href="https://github.com/cockroachlabs/Docker-CRDB/blob/main/images/alerting.png" rel="noopener noreferrer"&gt;Alerting architecture (GitHub)&lt;/a&gt;



&lt;h3&gt;
  
  
  alerting key capabilities
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Monitoring and alerting using quantitative metrics from Prometheus, triggered when thresholds are reached or exceeded.&lt;/li&gt;
&lt;li&gt;Monitoring and alerting using events from text/string/JSON logs as our triggers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  the GitHub repository
&lt;/h2&gt;

&lt;p&gt;The folders represent all the operational containers running the complete solution. Specifically there is a 1:1 relationship between each folder in github and each container in the architecture diagram, and this was intentional to make it easy to learn, to locate and make adjustments to any component independently of all the others.&lt;/p&gt;

&lt;h3&gt;
  
  
  configuration files &amp;amp; settings
&lt;/h3&gt;

&lt;p&gt;The architecture diagram highlights each configuration file that governs that particular aspect of this platform, and they can be found in the corresponding github folders.  While everything is wired together right out of the box using typical values, you have full control of all the settings and communication.  This facilitates an easy and flexible integration into an existing Cockroach environment, and as a bonus you get to learn how all the pieces work together.&lt;/p&gt;

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



&lt;p&gt;The key aspect in this organization is that each folder contains a docker-compose yaml file that defines the container including hostnames, images to use, which ports to expose and map publicly. You run &lt;strong&gt;docker-compose up -f&lt;/strong&gt; and this will build the container using the image along with the properties in the spec.  This container is then pushed into your local Docker repository and launched in your Docker environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  implementation details
&lt;/h2&gt;

&lt;p&gt;The GitHub repo covers the remaining details such as tools/prerequisites, establishing a Docker bridge network, and creating certificates for CockroachDB.&lt;/p&gt;

&lt;p&gt;Cheat-sheets, fragments, and command shortcuts are included with example values to help stand-up the environment quickly.&lt;/p&gt;

&lt;p&gt;Finally the startup sequence of containers and a listing of the endpoints are given for convenience.  Note that the endpoints have defined ports based on the default settings of this project.&lt;/p&gt;

&lt;h3&gt;
  
  
  running CockroachDB and Grafana
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;We need to create data-source connections from our log sources into Grafana. According to the architecture diagram, we’ll establish the Loki and Prometheus endpoints in the Grafana UI as shown in this screenshot:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8zcq2d8h3o2faotg3ub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8zcq2d8h3o2faotg3ub.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
data source definitions in the Grafana UI



&lt;ol&gt;
&lt;li&gt;We need to define a default &lt;strong&gt;&lt;em&gt;contact point&lt;/em&gt;&lt;/strong&gt; which includes a webhook URL to the &lt;strong&gt;&lt;em&gt;alerts&lt;/em&gt;&lt;/strong&gt; container:&lt;/li&gt;
&lt;/ol&gt;

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



&lt;ol&gt;
&lt;li&gt;Define the alert parameters that are sent as a payload to the &lt;strong&gt;&lt;em&gt;alerts&lt;/em&gt;&lt;/strong&gt; container. Note that the SMS_Key field syntax must have the format '&amp;lt;SID&amp;gt;:&amp;lt;Secret&amp;gt;'.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvau9rl6o6w935x1w4y6x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvau9rl6o6w935x1w4y6x.png" alt="Image description"&gt;&lt;/a&gt;Alert container parameters as required by the alerting NodeJS app&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Log Queries
&lt;/h2&gt;

&lt;p&gt;Below are a few example queries that you can test out against your CockroachDB cluster. &lt;/p&gt;

&lt;h3&gt;
  
  
  Prometheus log query examples
&lt;/h3&gt;

&lt;p&gt;A: Alerting Rule Query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rate(sql_distsql_contended_queries_count{instance=~"crdb-node01:8080|crdb-node02:8080|crdb-node03:8080"}[3m:10s])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;B: When this rate is &amp;gt; 0.373&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9elb8bqmrmf52y13bxv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9elb8bqmrmf52y13bxv.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
This example shows the activity and triggered alert during our load-test.



&lt;p&gt;The CRDB admin console provides basic views to the prometheus data, such as "SQL Statements" (queries) and "SQL Statement Contention". These charts can be replicated in Grafana using the following queries:&lt;/p&gt;

&lt;h4&gt;
  
  
  SQL Statements (queries)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rate(sql_update_count{instance=~"crdb-node01:8080|crdb-node02:8080|crdb-node03:8080"}[1m:2s])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  SQL Statement Contention
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rate(sql_txn_contended_count{instance=~"crdb-node01:8080|crdb-node02:8080|crdb-node03:8080"}[1m:1s])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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



&lt;p&gt;Other examples that show interesting views into the data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rate(sql_mem_distsql_current{instance=~"crdb-node01:8080|crdb-node02:8080|crdb-node03:8080"}[5m:10s])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;admission_admitted_kv{instance=~"crdb-node01:8080|crdb-node02:8080|crdb-node03:8080"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rate(admission_admitted_kv{instance=~"crdb-node01:8080|crdb-node02:8080|crdb-node03:8080"}[2m:10s])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rate(sql_insert_count_internal{instance=~"crdb-node01:8080|crdb-node02:8080|crdb-node03:8080"}[3m:10s])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sql_contention_resolver_retries{instance=~"crdb-node01:8080|crdb-node02:8080|crdb-node03:8080"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rate(sql_contention_resolver_retries{instance=~"crdb-node01:8080|crdb-node02:8080|crdb-node03:8080"}[3m:10s])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sql_stats_mem_current{instance=~"crdb-node01:8080|crdb-node02:8080|crdb-node03:8080"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sql_contention_resolver_queue_size{instance=~"crdb-node01:8080|crdb-node02:8080|crdb-node03:8080"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Loki log query examples
&lt;/h3&gt;

&lt;p&gt;Loki digs into the cockroach logs folder, capturing all the text-based messages that occur within the database. This service is necessary to capture connectivity issues, gossip-protocol updates, system events, and other activities related to a distributed database.&lt;/p&gt;

&lt;p&gt;Exact case string-search:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{job=~"CRDB01|CRDB02|CRDB03"} |= "circuitbreaker"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Case insensitive &amp;amp; using regex line filters expressions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{job=~"CRDB01|CRDB02|CRDB03"} |~ "(?i)CircuitBREAKER"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Capture logs with circuit breakers and connection (issues) in the logs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{job=~"CRDB01|CRDB02|CRDB03"} |~ "(?i)Circuitbreaker" |~"connection"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rate of circuit breakers over a timeframe of 50 seconds&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rate( ( {job=~"CRDB01|CRDB02|CRDB03"} |~ "(?i)Circuitbreaker")[50s] ) 
rate( ( {job=~"CRDB01|CRDB02|CRDB03"} |~ "(?i)gossip")[20s] )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6ji7vik70m1jdyk8roj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6ji7vik70m1jdyk8roj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
This screenshot illustrates a sample query capturing the word “fail” (case-insensitive) and the trend-chart of occurrences, followed by the actual log-file entries with text-highlighting.



&lt;h2&gt;
  
  
  GitHub Repo URL
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/cockroachlabs/Docker-CRDB" rel="noopener noreferrer"&gt;github.com/cockroachlabs/Docker-CRDB&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.cockroachlabs.com/docs/v22.1/configure-logs.html" rel="noopener noreferrer"&gt;https://www.cockroachlabs.com/docs/v22.1/configure-logs.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.cockroachlabs.com/docs/v22.1/logging-use-cases.html" rel="noopener noreferrer"&gt;https://www.cockroachlabs.com/docs/v22.1/logging-use-cases.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.cockroachlabs.com/docs/v22.1/log-formats.html" rel="noopener noreferrer"&gt;https://www.cockroachlabs.com/docs/v22.1/log-formats.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/cockroachlabs/a-gentle-introduction-to-cockroachdb-logging-and-fluentd-4mn9"&gt;https://dev.to/cockroachlabs/a-gentle-introduction-to-cockroachdb-logging-and-fluentd-4mn9&lt;/a&gt;&lt;br&gt;
&lt;a href="https://grafana.com/docs/loki/latest/logql/query_examples" rel="noopener noreferrer"&gt;https://grafana.com/docs/loki/latest/logql/query_examples&lt;/a&gt;&lt;br&gt;
&lt;a href="https://grafana.com/blog/2021/05/05/how-to-search-logs-in-loki-without-worrying-about-the-case" rel="noopener noreferrer"&gt;https://grafana.com/blog/2021/05/05/how-to-search-logs-in-loki-without-worrying-about-the-case&lt;/a&gt;&lt;br&gt;
&lt;a href="https://megamorf.gitlab.io/cheat-sheets/loki" rel="noopener noreferrer"&gt;https://megamorf.gitlab.io/cheat-sheets/loki&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cockroachdb</category>
      <category>docker</category>
      <category>grafana</category>
    </item>
  </channel>
</rss>
