<?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: Micah Carrick</title>
    <description>The latest articles on DEV Community by Micah Carrick (@micahcarrick).</description>
    <link>https://dev.to/micahcarrick</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%2F466859%2F88e7b081-396d-48b1-8fa2-6416a9cdacc0.png</url>
      <title>DEV Community: Micah Carrick</title>
      <link>https://dev.to/micahcarrick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/micahcarrick"/>
    <language>en</language>
    <item>
      <title>Using a YubiKey with AWS CLI Sessions</title>
      <dc:creator>Micah Carrick</dc:creator>
      <pubDate>Sun, 23 Feb 2025 19:34:43 +0000</pubDate>
      <link>https://dev.to/micahcarrick/using-a-yubikey-with-aws-cli-sessions-3lk4</link>
      <guid>https://dev.to/micahcarrick/using-a-yubikey-with-aws-cli-sessions-3lk4</guid>
      <description>&lt;p&gt;This is the bash script I use with &lt;a href="https://developers.yubico.com/yubikey-manager/" rel="noopener noreferrer"&gt;yubikey-manager CLI (ykman)&lt;/a&gt; to create a session for the AWS CLI using a YubiKey as a MFA device. This configuration is specifically for using short-term credentials.&lt;/p&gt;

&lt;p&gt;Using the script avoids having to copy/paste the code obtained from the YubiKey to the &lt;a href="https://docs.aws.amazon.com/cli/latest/reference/sts/get-session-token.html" rel="noopener noreferrer"&gt;&lt;code&gt;get-session-token&lt;/code&gt;&lt;/a&gt; command.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://jqlang.org/" rel="noopener noreferrer"&gt;jq&lt;/a&gt; utility&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://aws.amazon.com/blogs/security/use-yubikey-security-key-sign-into-aws-management-console/" rel="noopener noreferrer"&gt;YubiKey MFA device configured for a AWS IAM user&lt;/a&gt; and it's serial number ARN&lt;/li&gt;
&lt;li&gt;AWS CLI configured for short-term credentials per &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html#getting-started-quickstart-new" rel="noopener noreferrer"&gt;Setting up the AWS CLI&lt;/a&gt;. For example:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;~/.aws/config&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[profile my-session]&lt;/span&gt;

&lt;span class="nn"&gt;[profile my-profile]&lt;/span&gt;
&lt;span class="py"&gt;source_profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;my-session&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script will first use &lt;code&gt;ykman&lt;/code&gt; which pauses and waits for the button on the YubiKey to be pressed. This produces a code that is passed to &lt;a href="https://docs.aws.amazon.com/cli/latest/reference/sts/get-session-token.html" rel="noopener noreferrer"&gt;&lt;code&gt;get-session-token&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;# MFA_SERIAL_ARN="arn:aws:iam::[ACCOUNT_ID]:mfa/[IAM_USER]"&lt;/span&gt;
&lt;span class="nv"&gt;MFA_SERIAL_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::111111111111:mfa/jane.doe"&lt;/span&gt;
&lt;span class="nv"&gt;USER_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-profile"&lt;/span&gt;
&lt;span class="nv"&gt;SESSION_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-session"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Fetching code from Yubikey device"&lt;/span&gt;
&lt;span class="nv"&gt;mfa_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ykman oath accounts code &lt;span class="nt"&gt;--single&lt;/span&gt; &lt;span class="nv"&gt;$MFA_SERIAL_ARN&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Creating session (code=&lt;/span&gt;&lt;span class="nv"&gt;$mfa_code&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
&lt;span class="nv"&gt;sts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws sts get-session-token &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--duration&lt;/span&gt; 14400 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--serial-number&lt;/span&gt; &lt;span class="nv"&gt;$MFA_SERIAL_ARN&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--token-code&lt;/span&gt; &lt;span class="nv"&gt;$mfa_code&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$USER_PROFILE&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$sts&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Credentials.AccessKeyId'&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nv"&gt;secret_access_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$sts&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Credentials.SecretAccessKey'&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nv"&gt;session_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$sts&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Credentials.SessionToken'&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nv"&gt;expiration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$sts&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Credentials.Expiration'&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Session expires on: &lt;/span&gt;&lt;span class="nv"&gt;$expiration&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
aws configure &lt;span class="nb"&gt;set &lt;/span&gt;aws_access_key_id &lt;span class="nv"&gt;$access_key_id&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$SESSION_PROFILE&lt;/span&gt;
aws configure &lt;span class="nb"&gt;set &lt;/span&gt;aws_secret_access_key &lt;span class="nv"&gt;$secret_access_key&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$SESSION_PROFILE&lt;/span&gt;
aws configure &lt;span class="nb"&gt;set &lt;/span&gt;aws_session_token &lt;span class="nv"&gt;$session_token&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$SESSION_PROFILE&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The output of the script would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fetching code from YubiKey device
Touch your YubiKey...
Creating session (code=123456)
Session expires on: 2025-02-23T22:12:29+00:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>aws</category>
      <category>iam</category>
      <category>mfa</category>
      <category>yubikey</category>
    </item>
    <item>
      <title>aerospike</title>
      <dc:creator>Micah Carrick</dc:creator>
      <pubDate>Fri, 13 Dec 2024 17:16:12 +0000</pubDate>
      <link>https://dev.to/micahcarrick/aerospike-580j</link>
      <guid>https://dev.to/micahcarrick/aerospike-580j</guid>
      <description></description>
      <category>database</category>
    </item>
    <item>
      <title>Custom Bluesky Handle on AWS with Terraform/OpenTofu</title>
      <dc:creator>Micah Carrick</dc:creator>
      <pubDate>Sat, 23 Nov 2024 17:12:23 +0000</pubDate>
      <link>https://dev.to/micahcarrick/custom-bluesky-handle-on-aws-with-terraformopentofu-46jb</link>
      <guid>https://dev.to/micahcarrick/custom-bluesky-handle-on-aws-with-terraformopentofu-46jb</guid>
      <description>&lt;p&gt;&lt;em&gt;How to set up your custom Bluesky handle using Terraform/OpenTofu with AWS Route53.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;In this post I'll show some example Terraform code to create DNS records in AWS Route53 to use &lt;a href="https://bsky.social/about/blog/3-6-2023-domain-names-as-handles-in-bluesky" rel="noopener noreferrer"&gt;Domain Names as Handles in Bluesky&lt;/a&gt;. This is not just for vanity, it is also one way to verify your account.&lt;/p&gt;

&lt;p&gt;While setting up a DNS record in Route53 is very easy to do using the AWS Console a la "click ops", many of us with a DevOps/SRE background have too many scars from manually provisioning infrastructure--even for our own personal projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bluesky.app/" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt; is built on the &lt;a href="https://atproto.com/" rel="noopener noreferrer"&gt;AT Protocol&lt;/a&gt;, a decentralized network for social applications. In the AT Protocol your &lt;a href="https://atproto.com/specs/handle" rel="noopener noreferrer"&gt;handle&lt;/a&gt; (eg. &lt;code&gt;your_handle.bsky.social&lt;/code&gt;) is a human-friendly identifier that links to a canonical, permanent &lt;a href="https://atproto.com/specs/did" rel="noopener noreferrer"&gt;decentralized identifier&lt;/a&gt; (aka &lt;code&gt;DID&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;In order to use a custom domain name for your handle (eg. &lt;code&gt;YOUR_DOMAIN.com&lt;/code&gt;) you create a DNS TXT record in which the host will be your handle and the record value resolves to your &lt;code&gt;DID&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;First, create a &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone" rel="noopener noreferrer"&gt;&lt;code&gt;aws_route53_zone&lt;/code&gt;&lt;/a&gt; if you do not already have a hosted zone for your domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route53_zone"&lt;/span&gt; &lt;span class="s2"&gt;"domain"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"YOUR_DOMAIN"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(replace &lt;code&gt;YOUR_DOMAIN&lt;/code&gt; with your top-level domain TLD)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next, find the DNS record value for your &lt;code&gt;DID&lt;/code&gt; as described in &lt;a href="https://bsky.social/about/blog/4-28-2023-domain-handle-tutorial" rel="noopener noreferrer"&gt;How to verify your Bluesky account&lt;/a&gt;. This value will looks something like &lt;code&gt;did=did:plc:YOUR_DID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Use this value in the list of records for a TXT type &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record" rel="noopener noreferrer"&gt;&lt;code&gt;aws_route53_record&lt;/code&gt;&lt;/a&gt; resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route53_record"&lt;/span&gt; &lt;span class="s2"&gt;"TXT_atproto"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;zone_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route53_zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone_id&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"_atproto.YOUR_DOMAIN"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"TXT"&lt;/span&gt;
  &lt;span class="nx"&gt;ttl&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
  &lt;span class="nx"&gt;records&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"did=did:plc:YOUR_DID"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(replace &lt;code&gt;YOUR_DOMAIN&lt;/code&gt; with your handle host name, and &lt;code&gt;YOUR_DID&lt;/code&gt; with your &lt;code&gt;DID&lt;/code&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;name&lt;/code&gt; attribute of this &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record" rel="noopener noreferrer"&gt;&lt;code&gt;aws_route53_record&lt;/code&gt;&lt;/a&gt; resource can be a TLD (eg. &lt;code&gt;YOUR_DOMAIN.com&lt;/code&gt;) or a subdomain (eg. &lt;code&gt;YOUR_HANDLE.YOUR_DOMAIN.com&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;After you &lt;a href="https://opentofu.org/docs/cli/commands/apply/" rel="noopener noreferrer"&gt;apply&lt;/a&gt; this Terraform/OpenTofu you can verify the DNS record using &lt;code&gt;dig&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dig TXT _atproto.YOUR_DOMAIN +short 
&lt;span class="s2"&gt;"did=did:plc:YOUR_DID"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, update your handle in your Bluesky account settings as described in &lt;a href="https://bsky.social/about/blog/4-28-2023-domain-handle-tutorial" rel="noopener noreferrer"&gt;How to verify your Bluesky account&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;You can find me on Bluesky as &lt;a href="https://bsky.app/profile/micah.carrick.social" rel="noopener noreferrer"&gt;@micah.carrick.social&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>bluesky</category>
      <category>atprotocol</category>
      <category>aws</category>
    </item>
    <item>
      <title>AWS Instances for Aerospike Clusters</title>
      <dc:creator>Micah Carrick</dc:creator>
      <pubDate>Sat, 30 Jan 2021 14:54:35 +0000</pubDate>
      <link>https://dev.to/aerospike/aws-instances-for-aerospike-clusters-1i2m</link>
      <guid>https://dev.to/aerospike/aws-instances-for-aerospike-clusters-1i2m</guid>
      <description>&lt;p&gt;&lt;em&gt;How I select the optimal AWS instance type for running Aerospike&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;One of the most common conversations I have when providing &lt;a href="https://www.aerospike.com/consulting-services/" rel="noopener noreferrer"&gt;consulting services&lt;/a&gt; for Aerospike customers running on AWS is about how to select the optimal AWS instance type.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which instance provides the lowest latency?&lt;/li&gt;
&lt;li&gt;Do I need a "storage optimized" instance type?&lt;/li&gt;
&lt;li&gt;Should I use EBS?&lt;/li&gt;
&lt;li&gt;Can I use a cheaper instance type?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When it comes to running production Aerospike workloads at scale on AWS I generally only look at a this short list of instance families:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Instance Family&lt;/th&gt;
&lt;th&gt;Strengths&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;i3en instances&lt;/td&gt;
&lt;td&gt;All the things! Big and fast with lots of storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;i3 instances&lt;/td&gt;
&lt;td&gt;Lots of storage at a low cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;r5/r5d instances&lt;/td&gt;
&lt;td&gt;Fast with lowest cost per GB of DRAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;c5d instances&lt;/td&gt;
&lt;td&gt;Very fast and low cost per vCPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;m5d instances&lt;/td&gt;
&lt;td&gt;Low-latency all-rounder&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So let's look closer at what I consider when selecting AWS instance types for Aerospike customers and then what each of these instance families is good at and where it might fall short.&lt;/p&gt;




&lt;h2&gt;
  
  
  Instance Type Considerations
&lt;/h2&gt;

&lt;p&gt;None of the technical considerations matter without cost. If price were no object I'd just say run Aerospike on a big ol' cluster consisting of the beefy &lt;code&gt;i3en&lt;/code&gt; instances and be done with it.&lt;/p&gt;

&lt;p&gt;But alas, we live in the real world...&lt;/p&gt;

&lt;h3&gt;
  
  
  Storage
&lt;/h3&gt;

&lt;p&gt;The first consideration for Aerospike on AWS is storage. Aerospike's &lt;a href="https://www.aerospike.com/docs/architecture/storage.html" rel="noopener noreferrer"&gt;Hybrid Storage&lt;/a&gt; configured to use SSD/Flash is going to be used in the vast majority of use cases.&lt;/p&gt;

&lt;p&gt;This means I want fast, consistent, and reliable NVMe SSD storage. Amazon's network-attached storage, Elastic Block Store (EBS), does not meet that need. EBS is fantastic for many storage needs, but it is not fast, consistent, or reliable when milliseconds matter. No, what I need is Amazon's &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ssd-instance-store.html" rel="noopener noreferrer"&gt;SSD instance store volumes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But, not all instance store volumes are equal. Aerospike has &lt;a href="https://www.aerospike.com/docs/operations/plan/ssd/ssd_certification.html#cloud-based-flash" rel="noopener noreferrer"&gt;certified the throughput of AWS SSD volumes&lt;/a&gt; for various instance families. This provides a good baseline for the relative performance expected from any given instance type.&lt;/p&gt;

&lt;p&gt;As a general rule I try to avoid instance sizes that don't make use of a full sized SSD for a given family. That means that I try not to go smaller than the smallest instance type within a family that has the same size SSD as the largest in that family (half-size instances and above). This is to avoid sharing the physical SSD with other tenants on the underlying AWS host.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory
&lt;/h3&gt;

&lt;p&gt;When Aerospike's Hybrid Storage is configured to store data in memory then the RAM is obviously the primary factor in the instance's storage capacity. When configured to store primary indexes in memory it's still a key factor, but, with some nuance.&lt;/p&gt;

&lt;p&gt;Aerospike's primary indexes are a fixed size (64 bytes) per object. That means that if I am storing a large amount of small objects then I need a higher memory-to-disk ratio than if I have fewer large objects.&lt;/p&gt;

&lt;p&gt;I also want to condider optional features than can leverage RAM such as &lt;a href="https://www.aerospike.com/docs/reference/configuration/index.html#read-page-cache" rel="noopener noreferrer"&gt;read page cache&lt;/a&gt; and &lt;a href="https://www.aerospike.com/docs/architecture/secondary-index.html" rel="noopener noreferrer"&gt;secondary index&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Network
&lt;/h3&gt;

&lt;p&gt;The oft-overlooked network capacity is very important in cloud environments. &lt;/p&gt;

&lt;p&gt;Obviously the network throughput and latency is an important consideration. However, there is also &lt;a href="https://www.aerospike.com/docs/architecture/clustering.html" rel="noopener noreferrer"&gt;clustering&lt;/a&gt; and the application's usage pattern to consider. Does the application have an aggressive SLA? How will it behave when the network is down? What about when the network is slow or dropping packets?&lt;/p&gt;

&lt;p&gt;So a more consistent and reliable network results in a more stable cluster. Sure, Aerospike will automatically handle nodes leaving the cluster due to a flakey network, however, that self-healing means replicating data and replicating data means spending money on data transfer.&lt;/p&gt;

&lt;p&gt;This means instance &lt;em&gt;size&lt;/em&gt; matters in addition to the type. When I'm looking at the &lt;a href="https://aws.amazon.com/ec2/instance-types/" rel="noopener noreferrer"&gt;AWS instance type documentation&lt;/a&gt; I'm paying special attention to the "Networking Performance" column. When the value is "up to X" then it's a bursty shared network allocation and the &lt;a href="https://en.wikipedia.org/wiki/Cloud_computing_issues#Performance_interference_and_noisy_neighbors" rel="noopener noreferrer"&gt;noisy neighbor effect&lt;/a&gt; will be more pronounced.&lt;/p&gt;

&lt;h3&gt;
  
  
  CPU
&lt;/h3&gt;

&lt;p&gt;I avoid the "burstable" general-purpose instance families like the &lt;code&gt;t2&lt;/code&gt;/&lt;code&gt;t3&lt;/code&gt; instances for production workloads. It's too risky to be able to accidentally push up above the baseline performance and have unexpected disruptions when CPU credits run dry from bursting.&lt;/p&gt;

&lt;p&gt;But beyond that, CPU is rarely a primary consideration in selecting the right instance type. As instance types are sized up to accomodate storage, memory, or network, I more often find CPU to be under-utilized.&lt;/p&gt;

&lt;p&gt;This is an opportunity for optimization as there are a number of optional features that can take advantage of under-utilized CPU:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TLS encryption in transit and encryption at rest can improve security (IMHO encryption should be the rule, not the exception).&lt;/li&gt;
&lt;li&gt;Storage compression can reduce overall storage capacity which means more cost savings.&lt;/li&gt;
&lt;li&gt;Client-server compression on the wire can save money on AWS ingress/egress costs.&lt;/li&gt;
&lt;li&gt;Taking advantage of the rich set of APIs available for complex-data types (CDTs) can optimize for both cost and performance by offloading some of the data manipulation to the Aerospike nodes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, for use cases that are not using these features I consider whether or not there is an opportunity to make use of more CPU to optimize for cost, performance, or both. And then of course the inverse is also true. I make sure to consider CPU for use cases that are using these features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instance Type Selection
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;storage optimized&lt;/em&gt; instances are the obvious contender. Both the older &lt;code&gt;i3&lt;/code&gt; and the shiny new &lt;code&gt;i3en&lt;/code&gt; instance families are viable. But, the &lt;em&gt;compute optimized&lt;/em&gt; &lt;code&gt;c5&lt;/code&gt; instances and the &lt;em&gt;memory optimized&lt;/em&gt; &lt;code&gt;r5&lt;/code&gt; are great options depending on the use case, and the &lt;code&gt;m5&lt;/code&gt; instances are a solid choice from the &lt;em&gt;general purpose&lt;/em&gt; category.&lt;/p&gt;

&lt;h3&gt;
  
  
  i3en Instances
&lt;/h3&gt;

&lt;p&gt;When talking about &lt;em&gt;storage optimized&lt;/em&gt; AWS instances the &lt;code&gt;i3en&lt;/code&gt; is the big dog. I was pretty excited about these when AWS announced them just last year.&lt;/p&gt;

&lt;p&gt;First, they have a huge amount of SSD capacity at the lowest cost per GB. The largest &lt;code&gt;i3en.24xlarge&lt;/code&gt; weighs in at whopping 8 x 7.5 TB SSD volumes for 60 TB of storage per instance at ~$1.58 per GB per year¹.&lt;/p&gt;

&lt;p&gt;Second, they have pretty fast drives. &lt;a href="https://www.aerospike.com/docs/operations/plan/ssd/ssd_certification.html#cloud-based-flash" rel="noopener noreferrer"&gt;We've clocked one of these SSD drives at 162k TPS&lt;/a&gt;. When compared to the &lt;code&gt;i3&lt;/code&gt; at 33k TPS that's a significant improvement!&lt;/p&gt;

&lt;p&gt;When compared with the older &lt;code&gt;i3&lt;/code&gt; instances, these instances have more SSD storage at a lower cost per GB, more SSD throughput, more CPU, more network, and more RAM. So... moar!&lt;/p&gt;

&lt;p&gt;The down sides are that being relatively new they may not be available in all regions and they have a higher cost per GB of RAM than &lt;em&gt;memory optimized&lt;/em&gt; &lt;code&gt;r5&lt;/code&gt; instances.&lt;/p&gt;

&lt;p&gt;I like the &lt;code&gt;i3en&lt;/code&gt; instances for low-latency workloads storing large amounts of data with a memory-to-disk ratio on the &lt;code&gt;i3en&lt;/code&gt; instances that results in a storage-bound cluster. In other words, when the number of instances is selected based on how much data needs to be stored and the indexes will fit into the available memory.&lt;/p&gt;

&lt;p&gt;I also like the &lt;code&gt;i3en&lt;/code&gt; instances for a configuration in which indexes are stored on the SSD as well as the data ("all flash"). This allows for HUGE amounts of data (think hundreds of TB and beyond) without breaking the bank on DRAM.&lt;/p&gt;

&lt;p&gt;For the best performance the &lt;a href="https://www.aerospike.com/docs/operations/plan/ssd/ssd_op.html" rel="noopener noreferrer"&gt;SSD disks should be over-provisioned&lt;/a&gt; 20%.&lt;/p&gt;

&lt;h3&gt;
  
  
  i3 Instances
&lt;/h3&gt;

&lt;p&gt;Before the &lt;code&gt;i3en&lt;/code&gt; instances hit the scene these were the kings of SSD capacity. The largest &lt;code&gt;i3.16xlarge&lt;/code&gt; instance has 8 x 1.9 TB SSD volumes. That's 15.2 TB of storage per instance at ~$2.88 per GB per year¹.&lt;/p&gt;

&lt;p&gt;They also have a very good cost per GB of RAM.&lt;/p&gt;

&lt;p&gt;The down side is the drives are substantially slower and they have less vCPU than all the other instance families being considered. In short, they are the slowest.&lt;/p&gt;

&lt;p&gt;I like the &lt;code&gt;i3&lt;/code&gt; instances when &lt;code&gt;i3en&lt;/code&gt; is not available and storing large amounts of data. With a fairly low cost per GB of RAM &lt;em&gt;and&lt;/em&gt; a fairly low cost per GB of SSD storage, I also like the &lt;code&gt;i3&lt;/code&gt; as a cost-effective all-rounder for lower throughput workloads.&lt;/p&gt;

&lt;p&gt;For the best performance the &lt;a href="https://www.aerospike.com/docs/operations/plan/ssd/ssd_op.html" rel="noopener noreferrer"&gt;SSD disks should be over-provisioned&lt;/a&gt; 20%.&lt;/p&gt;

&lt;h3&gt;
  
  
  r5 and r5d Instances
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;memory optimized&lt;/em&gt; &lt;code&gt;r5&lt;/code&gt; instances provide the best cost per GB of RAM which makes them the ideal for when Aerospike is configured as an in-memory database. The largest &lt;code&gt;r5.24xlarge&lt;/code&gt; instances have 768 GB of RAM per instance at ~$78.84 per GB per year.&lt;/p&gt;

&lt;p&gt;However, the &lt;code&gt;r5d&lt;/code&gt; variant also adds 4 x 900 GB SSD volumes for a total of 3.6 TB per instance. These are fairly fast SSD drives which we've clocked at 138k TPS.&lt;/p&gt;

&lt;p&gt;The down side is that the &lt;code&gt;r5d&lt;/code&gt; variant has the &lt;em&gt;highest&lt;/em&gt; cost per GB of SSD storage of all the instance types considered here.&lt;/p&gt;

&lt;p&gt;I like the &lt;code&gt;r5&lt;/code&gt; instances for running Aerospike as an in-memory database and I like the &lt;code&gt;r5d&lt;/code&gt; instances for low-latency use cases where the memory-to-disk ratio results in a memory-bound cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  c5d Instances
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;compute optimized&lt;/em&gt; &lt;code&gt;c5d&lt;/code&gt; instances are all about speed. When latency is the be-all/end-all these are the front runner. They have the most powerful CPUs and the SSD volumes are very fast.&lt;/p&gt;

&lt;p&gt;The down sides are that they have the highest cost per GB of RAM by a wide margin and max out at 192 GB RAM per instance.&lt;/p&gt;

&lt;p&gt;I like the &lt;code&gt;c5d&lt;/code&gt; instances when the use case is very latency sensitive, is making use of CPU heavy features of Aerospike (encryption, compression, CDTs, etc.), and the memory-to-disk ratio does not result in a memory-bound cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Instance Comparison
&lt;/h3&gt;

&lt;p&gt;To do an objective comparison of these instance types based on a specific workload with specific performance requirements, I refer to the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/ec2/instance-types/" rel="noopener noreferrer"&gt;Amazon Instance Types&lt;/a&gt; and &lt;a href="https://aws.amazon.com/ec2/pricing/" rel="noopener noreferrer"&gt;Amazon EC2 Pricing&lt;/a&gt; provide up-to-date specs on the various instances and current pricing respectively.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.aerospike.com/docs/operations/plan/ssd/ssd_certification.html#cloud-based-flash" rel="noopener noreferrer"&gt;Aerospike Cloud-Based Flash ACT Results&lt;/a&gt; provides some baseline TPS numbers for various instances, however, I typically do a custom test using the open-source &lt;a href="https://github.com/aerospike/act" rel="noopener noreferrer"&gt;Aerospike Certification Tool (ACT)&lt;/a&gt;. This is the only way to get an accurate profile of the SSD's performance profile for a specific workload.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.aerospike.com/docs/operations/plan/capacity/" rel="noopener noreferrer"&gt;Aerospike Capacity Planning Guide&lt;/a&gt; provides details on how to calculate the RAM, SSD storage, and SSD throughput needs for a specific workload.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following table compares the maximum size instance types for each family on my short list. The cost is broken down into annual cost per some unit of capacity. This makes it easy to make a first pass at which instance familiy is going to be a good fit for a specific workload.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Instance&lt;/th&gt;
&lt;th&gt;vCPU&lt;/th&gt;
&lt;th&gt;SSD&lt;/th&gt;
&lt;th&gt;DRAM&lt;/th&gt;
&lt;th&gt;TPS²&lt;/th&gt;
&lt;th&gt;Hourly&lt;/th&gt;
&lt;th&gt;per GB SSD&lt;/th&gt;
&lt;th&gt;per GB DRAM&lt;/th&gt;
&lt;th&gt;per 1k TPS²&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;i3en.24xlarge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;96&lt;/td&gt;
&lt;td&gt;60.0 TB&lt;/td&gt;
&lt;td&gt;768 GB&lt;/td&gt;
&lt;td&gt;1,296k&lt;/td&gt;
&lt;td&gt;$10.848&lt;/td&gt;
&lt;td&gt;$1.58 /year&lt;/td&gt;
&lt;td&gt;$123.74 /year&lt;/td&gt;
&lt;td&gt;$73.32 /year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;i3.16xlarge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;15.2 TB&lt;/td&gt;
&lt;td&gt;488 GB&lt;/td&gt;
&lt;td&gt;264k&lt;/td&gt;
&lt;td&gt;$4.992&lt;/td&gt;
&lt;td&gt;$2.88 /year&lt;/td&gt;
&lt;td&gt;$89.61 /year&lt;/td&gt;
&lt;td&gt;$165.64 /year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;r5d.24xlarge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;96&lt;/td&gt;
&lt;td&gt;3.6 TB&lt;/td&gt;
&lt;td&gt;768 GB&lt;/td&gt;
&lt;td&gt;552k&lt;/td&gt;
&lt;td&gt;$6.912&lt;/td&gt;
&lt;td&gt;$16.82 /year&lt;/td&gt;
&lt;td&gt;$78.84 /year&lt;/td&gt;
&lt;td&gt;$109.69 /year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;c5d.24xlarge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;96&lt;/td&gt;
&lt;td&gt;3.6 TB&lt;/td&gt;
&lt;td&gt;192 GB&lt;/td&gt;
&lt;td&gt;564k&lt;/td&gt;
&lt;td&gt;$4.608&lt;/td&gt;
&lt;td&gt;$11.21 /year&lt;/td&gt;
&lt;td&gt;$210.24 /year&lt;/td&gt;
&lt;td&gt;$71.57 /year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;m5d.24xlarge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;96&lt;/td&gt;
&lt;td&gt;3.6 TB&lt;/td&gt;
&lt;td&gt;384 GB&lt;/td&gt;
&lt;td&gt;432k&lt;/td&gt;
&lt;td&gt;$5.424&lt;/td&gt;
&lt;td&gt;$13.20 /year&lt;/td&gt;
&lt;td&gt;$123.74 /year&lt;/td&gt;
&lt;td&gt;$109.99 /year&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A high-throughput workload without a small amount of data is likely to be most cost effective on &lt;code&gt;c5d&lt;/code&gt; instances as they have a low cost per 1k TPS.&lt;/li&gt;
&lt;li&gt;A high-throughput workload with a very large amount of data is likely to be most cost effective on &lt;code&gt;i3en&lt;/code&gt; instances as they have a low cost per 1k TPS but also have a lot of SSD storage.&lt;/li&gt;
&lt;li&gt;A high-throughput workload with a lot of small objects, and thus a high memory-to-disk ratio, is likely to be most cost effective on &lt;code&gt;r5d&lt;/code&gt; instances as they have a low cost per GB of DRAM.&lt;/li&gt;
&lt;li&gt;A low-throughput workload with a lot of data with a moderate/high number of objects is likely to be most cost effective on &lt;code&gt;i3&lt;/code&gt; instances as they have both low cost per GB of DRAM as well as low cost per GB of SSD storage. &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;¹ &lt;em&gt;Cost estimates based on largest instance type in family using the full list pricing for on-demand instances in us-east-1 as of September 2020&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;² &lt;em&gt;Per-instance TPS based on a single drive multiplied by total drives per instance. See details and caveats at &lt;a href="https://www.aerospike.com/docs/operations/plan/ssd/ssd_certification.html#cloud-based-flash" rel="noopener noreferrer"&gt;Cloud-Based Flash ACT results&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling Strategy (Instance Size vs. Cluster Size)
&lt;/h2&gt;

&lt;p&gt;The instance type selection pointed out that the smaller end of the sizes within an instance family have bursty network and shared SSD controllers due to the amount of other tenants on the host. What this amounts to is these instance sizes will have more &lt;em&gt;variability&lt;/em&gt; in their performance characteristics.&lt;/p&gt;

&lt;p&gt;For smaller workloads there must be a trade off between larger instance sizes and larger cluster sizes. The consideration here is about having enough instances in the cluster to spread the workload out which lessens the impact of single instance maintenance or failure versus minimizing the variability of the performance. On the other end of the spectrum as the cluster grows more nodes can mean longer maintenance for rolling updates and larger instances mean slower cold restarts as indexes are being rebuilt.&lt;/p&gt;

&lt;p&gt;There is no hard and fast rule here and these considerations must be taken in context of each organization's budget and operational parameters, however, the approach I take for production clusters is loosely:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prefer a cluster size of 2x availability zones and don't go less than 1x&lt;/li&gt;
&lt;li&gt;Prefer instance sizes with fixed network performance and don't go less than 4 vCPU&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So that means if I'm designing a production cluster in 3 zones based on the &lt;code&gt;r5d&lt;/code&gt; family the smallest cluster I would recommend is 3x &lt;code&gt;r5d.xlarge&lt;/code&gt; (4vCPU).&lt;/p&gt;

&lt;p&gt;The scaling strategy would be to scale &lt;em&gt;out&lt;/em&gt; to 6x &lt;code&gt;r5d.xlarge&lt;/code&gt; to get to 2x zones after which it would then scale &lt;em&gt;up&lt;/em&gt; until it gets to the &lt;code&gt;r5d.8xlarge&lt;/code&gt; which have the fixed network.&lt;/p&gt;

&lt;p&gt;However, if the workload is latency sensitive and the variability of the small instances is a concern then the scaling strategy would insted be to scale &lt;em&gt;up&lt;/em&gt; from the minimum cluster size of 3 until it gets to 3x &lt;code&gt;r5d.8xlarge&lt;/code&gt; where it has the fixed network. Then it would start scaling &lt;em&gt;out&lt;/em&gt; to 6x &lt;code&gt;r5d.8xlarge&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once reaching the point of 6x &lt;code&gt;r5d.8xlarge&lt;/code&gt; scaling &lt;em&gt;up&lt;/em&gt; to the &lt;code&gt;r5d.12xlarge&lt;/code&gt; would be the next step to get to the full-size SSD (not shared). After that the decisions to continue to scale &lt;em&gt;up&lt;/em&gt; vs. &lt;em&gt;out&lt;/em&gt; are largely dependent on the opertational parameters that best suite the organization.&lt;/p&gt;




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

&lt;p&gt;The art of selecting the right instance type is about optimizing the cost to performance ratio.&lt;/p&gt;

&lt;p&gt;One of the beautiful things about running Aerospike in the cloud is that you don't have to get it perfect on day one. You can start with a conservative approach in which you leave room for the application usage patterns to evolve and stabilize before right-sizing based on real-world production metrics. Remember, with Aerospike you can always do rolling updates, with zero downtime, to switch between instance sizes and types or do A/B testing.&lt;/p&gt;

</description>
      <category>aerospike</category>
      <category>aws</category>
      <category>nosql</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Python argument parsing with log level and config file</title>
      <dc:creator>Micah Carrick</dc:creator>
      <pubDate>Tue, 15 Sep 2020 11:56:36 +0000</pubDate>
      <link>https://dev.to/micahcarrick/python-argument-parsing-with-log-level-and-config-file-1d6p</link>
      <guid>https://dev.to/micahcarrick/python-argument-parsing-with-log-level-and-config-file-1d6p</guid>
      <description>&lt;p&gt;&lt;em&gt;Making use of &lt;code&gt;parse_known_args()&lt;/code&gt; to set root log level and parse default values from a configuration file before parsing main args.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Suppose you're banging out a simple command-line utility in Python which has the following requirements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Root log level can be set with a command-line argument&lt;/li&gt;
&lt;li&gt;Configuration is &lt;em&gt;optionally&lt;/em&gt; read from an external file&lt;/li&gt;
&lt;li&gt;Command-line arguments take precedence over configuration file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;argparse.ArgumentParser&lt;/code&gt; class includes the &lt;code&gt;parse_known_args()&lt;/code&gt; method which allows for incrementally parsing the command-line arguments such that you can setup how subsequent arguments are parsed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set log level from command-line argument
&lt;/h2&gt;

&lt;p&gt;The root logger must be configured &lt;em&gt;before&lt;/em&gt; there is any output. If the argument parsing is going to be doing work that could or should produce log messages but you want the log level to be set from the arguments being parsed, use &lt;code&gt;parse_known_args()&lt;/code&gt; to parse and set the log level first. Then proceed with the rest of the arguments.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As an aside, I'm generally a fan of using &lt;a href="https://docs.python.org/3/library/logging.config.html#logging-config-fileformat" rel="noopener noreferrer"&gt;logging config files&lt;/a&gt; to setup more robust logging for Python applications. However, for quick 'n simple command-line scripts I like the simplicity of this approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parse defaults from configuration file
&lt;/h2&gt;

&lt;p&gt;A similar technique can be used to parse a configuration filename from the command-line arguments and then use that configuration file to set the default values for the remaining arguments being parsed.&lt;/p&gt;

&lt;p&gt;Suppose a configuration file contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[options]
option1=config value
option2=config value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The way this works, is the default values for subsequent commmand-line arguments are defined in a dictionary named &lt;code&gt;defaults&lt;/code&gt; rather than using the &lt;code&gt;default&lt;/code&gt; keyword argument with &lt;code&gt;add_argument()&lt;/code&gt;. Then, the &lt;code&gt;parse_known_args()&lt;/code&gt; is used to parse the configuration filename from the command line arguments. If it is present then it reads the values out of the configuration and updates the &lt;code&gt;defaults&lt;/code&gt; dictionary. Then those defaults are applied to the remaining command-line arguments with the &lt;code&gt;set_defaults()&lt;/code&gt; method.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;p&gt;See the &lt;a href="https://github.com/MicahCarrick/micahcarrick-posts/blob/master/python-argparse-configfile-loglevel/example.py" rel="noopener noreferrer"&gt;complete example.py source code&lt;/a&gt; which combines both of the above techniques.&lt;/p&gt;

&lt;p&gt;Override default log level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./example.py -l DEBUG
INFO:__main__:Log level set: DEBUG
INFO:__main__:Option 1: default value
INFO:__main__:Option 2: default value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read values from configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./example.py -l DEBUG -c example.conf
INFO:__main__:Log level set: DEBUG
INFO:__main__:Loading configuration: example.conf
INFO:__main__:Option 1: config value
INFO:__main__:Option 2: config value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Override values with command-line arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./example.py -l DEBUG -c example.conf -1 "cli value"
INFO:__main__:Log level set: DEBUG
INFO:__main__:Loading configuration: example.conf
INFO:__main__:Option 1: cli value
INFO:__main__:Option 2: config value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>python</category>
    </item>
    <item>
      <title>Aerospike Security Events and Audit Logs</title>
      <dc:creator>Micah Carrick</dc:creator>
      <pubDate>Mon, 14 Sep 2020 23:22:57 +0000</pubDate>
      <link>https://dev.to/micahcarrick/aerospike-security-events-and-audit-logs-25ok</link>
      <guid>https://dev.to/micahcarrick/aerospike-security-events-and-audit-logs-25ok</guid>
      <description>&lt;p&gt;In the &lt;strong&gt;Enterprise Database Security&lt;/strong&gt; session I presented at &lt;a href="https://www.aerospike.com/summit/" rel="noopener noreferrer"&gt;Aerospike Summit 2020&lt;/a&gt; I gave an overview of data protection with Aerospike Enterprise.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://cdn.carrick.tech/micahcarrick-posts/aerospike-summit20-enterprise-database-security/Aerospike%20Summit%202020%20-%20Enterprise%20Database%20Security.pdf" rel="noopener noreferrer"&gt;Download presentation PDF&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;To provide context, refer to the following diagram depicting an Aerospike deployment.&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%2Fjld2wqhhfzccibo0y2k6.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%2Fjld2wqhhfzccibo0y2k6.png" alt="Aerospike Deployment Diagram" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once all the enterprise security fetures have been implemented, how do we verify we’re doing any of this right? How do we get visibility into what’s happened in the past and how do we respond to events as they happen in real time?&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Event Architecture
&lt;/h2&gt;

&lt;p&gt;The diagram below is depicting an Aerospike node on the left producing a security audit trail and shipping that to a downstream system via syslog. The rest of this diagram is just one of many types of architectures for consuming Aerospike audit logs. I’ll talk this one through to give you an idea of what’s happening.&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%2Fnw2fc0hfyaqfno5zbft0.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%2Fnw2fc0hfyaqfno5zbft0.png" alt="Aerospike Security Event Audit Log Architecture" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, the audit trail from Aerospike is separate from the standard server logs used for troubleshooting and analysis. It includes events that are relevant for security monitoring such as authentication events, user administration, system administration, etc.&lt;/p&gt;

&lt;p&gt;The audit trail is shipped using the syslog protocol to some type of log collection like syslog-ng, rsyslog, the Elastic stack, Splunk agents, etc. &lt;/p&gt;

&lt;p&gt;There is often a highly scalable queuing system, something like Kafka, in between the log producers and collectors and the downstream consumers. This avoids tight coupling between the systems allowing producing and consuming at different rates and independently maintaining system components.&lt;/p&gt;

&lt;p&gt;There is also typically events and data from other sources being brought in and then ingested by the SIEM and/or log analysis platforms. It is in these platforms that all of this security data can be monitored in real time to detect and respond to potential security threats or data breaches. In addition to the real-time monitoring, this also provides security professionals with historical data to use for forensics, audits, training new M/L models, etc.&lt;/p&gt;

&lt;p&gt;And one final note about the Aerospike audit trail is that what events are logged is configurable. It is possible to ship every single data operation, including all reads and writes. In this way which application or user made what change at what time is auditable. This is a very common requirement from the data privacy and compliance side.&lt;/p&gt;

&lt;p&gt;However, I often find that enterprises make exceptions when the scale becomes impractical for the downstream systems. Imagine an Aerospike cluster handling tens of millions of operations per second in a very cost effective way, and then shipping all those events to a downstream system that isn’t designed to scale, doesn’t scale linearly, can’t handle the real-time ingestion, or isn’t cost effective at scale. This comes down to a risk-based business decision and in many cases organizations rely on compensating controls in the application and system access controls to achieve the auditability of data access that they require.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.aerospike.com/docs/operations/configure/security/access-control/index.html#audit-trails" rel="noopener noreferrer"&gt;Aerospike - Audit Trails&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aerospike</category>
      <category>nosql</category>
      <category>security</category>
    </item>
    <item>
      <title>Aerospike Data Protection</title>
      <dc:creator>Micah Carrick</dc:creator>
      <pubDate>Mon, 14 Sep 2020 23:22:33 +0000</pubDate>
      <link>https://dev.to/micahcarrick/aerospike-data-protection-1odc</link>
      <guid>https://dev.to/micahcarrick/aerospike-data-protection-1odc</guid>
      <description>&lt;p&gt;In the &lt;strong&gt;Enterprise Database Security&lt;/strong&gt; session I presented at &lt;a href="https://www.aerospike.com/summit/" rel="noopener noreferrer"&gt;Aerospike Summit 2020&lt;/a&gt; I gave an overview of data protection with Aerospike Enterprise.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://cdn.carrick.tech/micahcarrick-posts/aerospike-summit20-enterprise-database-security/Aerospike%20Summit%202020%20-%20Enterprise%20Database%20Security.pdf" rel="noopener noreferrer"&gt;Download presentation PDF&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;To provide context, refer to the following diagram depicting an Aerospike deployment.&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%2Frt8t1ntebk5p2859xg7a.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%2Frt8t1ntebk5p2859xg7a.png" alt="Aerospike Deployment Diagram" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even after securing the network and implementing authentication and authorization, remember that we're persisting this data on physical devices somewhere. Even in the cloud there’s still racks and servers behind all that "magic".&lt;/p&gt;

&lt;p&gt;So how do we protect that persisted data from unauthorized access and how do we reduce the attack surface?&lt;/p&gt;




&lt;h2&gt;
  
  
  Data Isolation and Encryption
&lt;/h2&gt;

&lt;p&gt;For protecting data at rest, that is, data persisted in the storage layer, we want to look at how we do data isolation and encryption in Aerospike.&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%2Fdtyt2dhd8n4wsfr8vq27.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%2Fdtyt2dhd8n4wsfr8vq27.png" alt="Aerospike Encryption at Rest" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram above depicts two SSD devices physically attached to an Aerospike server node. These are &lt;em&gt;physical devices&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In this example, each SSD device is also divided into two logical partitions for a total of 4 partitions. These are &lt;em&gt;logical devices&lt;/em&gt; and data stored on one partition is logically separate from data in the other partitions.&lt;/p&gt;

&lt;p&gt;One of the authorization scopes we can apply to a permission with &lt;a href="https://www.aerospike.com/docs/operations/configure/security/access-control/index.html" rel="noopener noreferrer"&gt;Aerospike’s access controls&lt;/a&gt; is &lt;code&gt;namespace&lt;/code&gt;. Namespaces not only provide scope for access controls, they also configure the storage layer. That configuration includes which physical or logical devices the data is persisted to, enabling AES encryption, and which key is used to encrypt and decrypt that data.&lt;/p&gt;

&lt;p&gt;So what that means is, from a data protection standpoint, each namespace provides data isolation from other namespaces. If the user credentials or the encryption key for Namespace #1 were to be compromised, the data stored in Namespace #2 is protected separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  OS Hardening and System Access Controls
&lt;/h2&gt;

&lt;p&gt;The data is encrypted in transit with TLS and encrypted at rest using AES encryption on the namespace. So far so good.&lt;/p&gt;

&lt;p&gt;However, the Aerospike process itself is obviously working with the unencrypted data in memory. Standard &lt;a href="https://en.wikipedia.org/wiki/Hardening_%28computing%29" rel="noopener noreferrer"&gt;OS/system hardening&lt;/a&gt; procedures and system access controls are absolutely critical for Aerospike deployments that store sensitive data. I’m not going to get into Linux system hardening as it’s tangential and there are plenty of industry standard tools, processes, and requirements on that front. But I do want to emphasise a best practice:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Keep Aerospike nodes as singular in purpose.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In general we don’t recommend running Aerospike along side any other non-related applications, but it’s especially important when working with sensitive data. It may be tempting or convenient to run some tools or dashboards directly on one of the Aerospike nodes but, that just increases the surface area for potential vulnerabilities. &lt;/p&gt;

&lt;p&gt;The tools or dashboards should not have direct access to sensitive data and therefore should not be running on an Aerospike node.&lt;/p&gt;

&lt;p&gt;A less obvious example of this is the &lt;a href="https://www.aerospike.com/docs/tools/" rel="noopener noreferrer"&gt;Aerospike Tools&lt;/a&gt; package which includes the &lt;code&gt;aql&lt;/code&gt;, &lt;code&gt;asinfo&lt;/code&gt; and &lt;code&gt;asadm&lt;/code&gt; commands among others. These administrative tools are bundled with the Aerospike Server and they are required to be run directly on the Aerospike nodes for a subset of operational tasks. Obtaining a collectinfo is a good example of this.&lt;/p&gt;

&lt;p&gt;However, the tools are also available as a stand-alone package and can be run from a remote server for most tasks. So a best practice is to make the Aerospike Tools available to authorized users on dedicated nodes specifically for that purpose. You will still want an escalation path that allows for node-level diagnostics and troubleshooting, however, that should be the exception and not the rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Secrets Management
&lt;/h2&gt;

&lt;p&gt;Protecting secrets in general is a very broad topic. So to keep things brief here I just want to enumerate the secrets associated with Aerospike that need to be protected and a couple of common patterns for how that’s done in a Production environment.&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%2Fb8uvdc5hltyriigl9w17.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%2Fb8uvdc5hltyriigl9w17.png" alt="Aerospike Secrets Management" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On any given Aerospike server node you may have TLS private keys, encryption-at-rest keys, external authentication (LDAP) credentials, and Cross-Datacent Replication (XDR) credentials. All of these secrets must be protected.&lt;/p&gt;

&lt;p&gt;These secrets are essentially bits of configuration that you are managing. They are keys and passwords that need to be available to the Aerospike process (&lt;code&gt;asd&lt;/code&gt;) at startup or at runtime. Once those secrets make it to the server your OS hardening and system access controls are in place to protect them. However, the challenge is in managing the full lifecycle of those secrets. They have to be created, they have to get deployed onto the servers, they may need to be revoked, and they will need to be rotated periodically.&lt;/p&gt;

&lt;p&gt;This is a problem well suited for &lt;em&gt;secrets management&lt;/em&gt; tools. Most enterprise security platforms have secrets management built in, all the major cloud providers have dedicated secrets management services, and open-source tools like &lt;a href="https://www.vaultproject.io/" rel="noopener noreferrer"&gt;Vault by Hashicorp&lt;/a&gt; have a vast array of Enterprise features and wide adoption. So let’s discuss a couple of patterns for using secrets management for Aerospike keys and credentials.&lt;/p&gt;

&lt;p&gt;The first pattern, shown as the top-half of the diagram above, is to integrate secrets management software into the configuration management workflow. For example, config management tools such as Ansible, Chef, Puppet, etc. can be set up to bring in secrets from the secret store when configuring a node. Aerospike loads the secrets from the filesystem and is completely decoupled from the secrets management system. This has the advantage of being straightforward to setup and compatible with just about any secrets management tool out there. However, it does result in any given secret being in 2 locations; once in the secret store and once on the Aerospike server. Secret lifecycle management for Aerospike secrets is always a 2-step process of updating the secret store and then running the configuration management tool.&lt;/p&gt;

&lt;p&gt;The second pattern, shown as the bottom-half of this diagram, is for Aerospike to integrate directly with the secrets management system. However, this requires Aerospike compatibility with a specific secret store. At this point in time Aerospike supports just one direct integration with a secrets management platform and that is the &lt;a href="https://www.hashicorp.com/integrations/aerospike/vault" rel="noopener noreferrer"&gt;Vault Integration with Aerospike&lt;/a&gt;. Vault in turn integrates with a large number of other systems.&lt;/p&gt;

&lt;p&gt;This pattern has the advantage of centrally managing secrets. When implemented correctly it lowers the credential management burden and lowers the risk of compromised secrets.&lt;/p&gt;

&lt;p&gt;However, being a direct integration, this pattern requires that the secret store is able to meet availability and scalability requirements. This means that this pattern is going to be a more complex architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.aerospike.com/docs/operations/configure/security/encryption-at-rest/index.html" rel="noopener noreferrer"&gt;Aerospike - Configuring Encryption at Rest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.aerospike.com/docs/operations/configure/security/vault/index.html" rel="noopener noreferrer"&gt;Aerospike - Hashicorp Vault Integration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aerospike</category>
      <category>nosql</category>
      <category>security</category>
    </item>
    <item>
      <title>Aerospike Authentication and Authorization</title>
      <dc:creator>Micah Carrick</dc:creator>
      <pubDate>Mon, 14 Sep 2020 23:22:06 +0000</pubDate>
      <link>https://dev.to/micahcarrick/aerospike-authentication-and-authorization-f0c</link>
      <guid>https://dev.to/micahcarrick/aerospike-authentication-and-authorization-f0c</guid>
      <description>&lt;p&gt;In the &lt;strong&gt;Enterprise Database Security&lt;/strong&gt; session I presented at &lt;a href="https://www.aerospike.com/summit/" rel="noopener noreferrer"&gt;Aerospike Summit 2020&lt;/a&gt; I gave an overview of authentication and authorization with Aerospike Enterprise.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://cdn.carrick.tech/micahcarrick-posts/aerospike-summit20-enterprise-database-security/Aerospike%20Summit%202020%20-%20Enterprise%20Database%20Security.pdf" rel="noopener noreferrer"&gt;Download presentation PDF&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;To provide context, refer to the following diagram depicting an Aerospike deployment.&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%2Fiov3j7nwmb0g8wrkcrai.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%2Fiov3j7nwmb0g8wrkcrai.png" alt="Aerospike Deployment Diagram" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the left we have developers building applications and back office jobs that will use an Aerospike database.&lt;/p&gt;

&lt;p&gt;In the middle we have an Aerospike cluster managed by one or more administrative groups such as SREs, DevOps, DBAs, etc.&lt;/p&gt;

&lt;p&gt;And on the right we have downstream systems which ingest and analyze security events and log data for use by information security teams.&lt;/p&gt;

&lt;p&gt;The red group icons depict actors that need to interact with the Aerospike database in some way. How do we control who and what can access this data and how do we manage that within an existing enterprise architecture?&lt;/p&gt;

&lt;p&gt;Identity and Access Management is a huge part of any enterprise IT organization. Aerospike includes a framework for authentication and authorization out-of-the-box, but it also integrates into your existing IAM infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication (AuthN)
&lt;/h2&gt;

&lt;p&gt;Both humans users &lt;em&gt;and&lt;/em&gt; machines (applications) need to authenticate when connecting to Aerospike Enterprise.&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%2Frfp7pria6gzuo5kzayjo.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%2Frfp7pria6gzuo5kzayjo.png" alt="Aerospike Internal Authentication" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may be pretty familiar with the concept of a human user having to authenticate with a database. You know, it’s like &lt;code&gt;GRANT some_permission to USER 'micah' on some_resource&lt;/code&gt;. But let’s touch on &lt;em&gt;applications&lt;/em&gt; needing to authenticate.&lt;/p&gt;

&lt;p&gt;As discussed as part of &lt;em&gt;Aerospike Network Security&lt;/em&gt;, the nodes the applications are running on have been authenticated with TLS. But that was about network security. Is &lt;em&gt;this server&lt;/em&gt; allowed to communicate with &lt;em&gt;that server&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Now we want to authenticate the &lt;em&gt;application&lt;/em&gt; running on that network so that we can later control what the application is authorized to do. In other words, we’re not trying to determine whether that application is allowed to communicate, we’re trying to determine &lt;em&gt;which&lt;/em&gt; application is communicating so we can later control what it is allowed to do. And to do that, we need that application to be authenticated--to identify itself.&lt;/p&gt;

&lt;p&gt;Right out of the box you can enable Aerospike’s internal authentication, which is shown above. Both humans and applications present a username and password combination when connecting to Aerospike and all the user management is done directly within Aerospike.&lt;/p&gt;

&lt;p&gt;This works for simple use cases and it’s a no-brainer to setup. However, every organization has their own unique set of IAM requirements. Things like password policies, credential lifecycle management, MFA, etc. The nuances and complexity of such systems is best delegated to the purpose-built tools already established in the enterprise IT infrastructure. So Aerospike supports integrating into these systems through &lt;em&gt;external authentication&lt;/em&gt;.&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%2Fy8hzz137y8f0i62xg3td.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%2Fy8hzz137y8f0i62xg3td.png" alt="Aerospike External Authentication" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the external authentication setup, Aerospike will delegate the credential check to the 3rd party system. In this case we’re looking at a typical directory to which Aerospike is integrated via LDAP. After a successful authentication, Aerospike will use an access token to authenticate subsequent connections for the lifetime of that token and then go back to the LDAP server as needed to re-authenticate.&lt;/p&gt;

&lt;p&gt;This is a very common setup for human users of the database and in some cases applications as well.&lt;/p&gt;

&lt;p&gt;However, with this architecture, the directory is in the critical path for the functionality of the applications using Aerospike. The LDAP directory may not be designed with the same availability, performance, or scale that an application is being designed for, so it may not be viable for all use cases.&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%2Fdydvmi4zincy3z96twt5.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%2Fdydvmi4zincy3z96twt5.png" alt="Aerospike Mixed Authentication" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now we can look at an pattern that combines both authentication methods. LDAP and the directory are still used for humans to authenticate, but the applications authenticate using the internal system. Now this removes the LDAP directory from the critical path, however, it presents a different problem. Part of the role of the directory in the external authentication setup was centralizing IAM.&lt;/p&gt;

&lt;p&gt;If users are managed directly in Aerospike, how are the credentials going to be provisioned, rotated, and revoked for the applications? How will the organization's policies and regulatory requirements be enforced?&lt;/p&gt;

&lt;p&gt;In smaller organizations or autonomous business units, this may not pose a large problem. But in larger enterprises this becomes untenable.&lt;/p&gt;

&lt;p&gt;This is where secrets management and dynamic credentials can help. Rather than the applications themselves having credentials to Aerospike, the applications query the secrets management system to obtain the credentials--often short lived credentials to lower risk.&lt;/p&gt;

&lt;p&gt;The secrets management system has the access necessary to manage the full lifecycle of Aerospike credentials and does so within the domain of the existing centralized IAM.&lt;/p&gt;

&lt;p&gt;With one of these three patterns we’ll have established which user or application is trying to do something, and can now authorize them or it to do so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authorization (AuthZ)
&lt;/h2&gt;

&lt;p&gt;We can apply access controls to the human users or applications by assigning them to Aerospike &lt;strong&gt;roles&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The role can then be allowed a set of &lt;strong&gt;privileges&lt;/strong&gt;. A privilege consists of a permission to perform some action along with a scope. For example, the permission to read data at a global scope would be one privilege and the permission to read data only for a specific &lt;em&gt;set&lt;/em&gt; in a specific &lt;em&gt;namespace&lt;/em&gt; would be a different privilege.&lt;/p&gt;

&lt;p&gt;This will allow for a least privileged access model in which any database user, be it a human user or application code, can be associated to roles that allow only the access necessary to perform their function.&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%2Fijj1ed87twioi6ym8o7f.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%2Fijj1ed87twioi6ym8o7f.png" alt="Aerospike Authorization" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s look at these examples using a hypothetical setup for Acme corp.&lt;/p&gt;

&lt;p&gt;First, the &lt;strong&gt;Acme IAM&lt;/strong&gt; role is for the administrative user or system, such as the secrets management system depicted in the previous slide, with a privilege to manage the full lifecycle of Aerospike users globally.&lt;/p&gt;

&lt;p&gt;Next, the &lt;strong&gt;Acme SRE&lt;/strong&gt; role in this example allows site reliability engineers to perform functions to address issues relating to system stability like querying server metrics, gracefully removing a node for maintenance, enabling different log levels, etc.&lt;/p&gt;

&lt;p&gt;Next the &lt;strong&gt;Acme DBA&lt;/strong&gt; role in this example allows database administrators to perform functions to optimize for the specific database use cases like managing secondary indexes, throttling scans, adding/removing user-defined functions, etc.&lt;/p&gt;

&lt;p&gt;The final 3 roles in this example, &lt;strong&gt;Acme App1&lt;/strong&gt;, &lt;strong&gt;Acme App2&lt;/strong&gt;, and &lt;strong&gt;Acme Daily Loader&lt;/strong&gt;, each allow the applications specific access to data, but scoped down to only that which is necessary for the function that application performs. For example, notice that the &lt;strong&gt;Acme App2&lt;/strong&gt; role can only read data from the set named &lt;code&gt;app2&lt;/code&gt; within the namespace &lt;code&gt;ns1&lt;/code&gt;. It will not be allowed to read data from the set that &lt;strong&gt;Acme App1&lt;/strong&gt; uses nor will it be able to write any data at all.&lt;/p&gt;

&lt;p&gt;So this is how you can set up some fine-grained role-based access control for users and applications.&lt;/p&gt;

&lt;p&gt;And finally, every role can be assigned a whitelist of IP CIDR ranges from which database users associated with that role can connect from. This provides an even finer level of granularity on top of the existing network security.&lt;/p&gt;

&lt;p&gt;For example, maybe a handful employees can all connect to Aerospike from their workstations within a particular private subnet, but only Alice and Bob can create new users and only when they do so from their specific personal workstations.&lt;/p&gt;

</description>
      <category>aerospike</category>
      <category>nosql</category>
      <category>security</category>
    </item>
    <item>
      <title>Aerospike Network Security</title>
      <dc:creator>Micah Carrick</dc:creator>
      <pubDate>Mon, 14 Sep 2020 20:14:12 +0000</pubDate>
      <link>https://dev.to/aerospike/aerospike-network-security-1pni</link>
      <guid>https://dev.to/aerospike/aerospike-network-security-1pni</guid>
      <description>&lt;p&gt;In the &lt;strong&gt;Enterprise Database Security&lt;/strong&gt; session I presented at &lt;a href="https://www.aerospike.com/summit/" rel="noopener noreferrer"&gt;Aerospike Summit 2020&lt;/a&gt; I gave an overview of network security with Aerospike Enterprise.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://cdn.carrick.tech/micahcarrick-posts/aerospike-summit20-enterprise-database-security/Aerospike%20Summit%202020%20-%20Enterprise%20Database%20Security.pdf" rel="noopener noreferrer"&gt;Download presentation PDF&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;To provide context, refer to the following diagram depicting an Aerospike deployment.&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%2F2oord0ezidowkqs9bzgv.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%2F2oord0ezidowkqs9bzgv.png" alt="Aerospike Deployment Diagram" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the left we have developers building applications and back office jobs that will use an Aerospike database.&lt;/p&gt;

&lt;p&gt;In the middle we have an Aerospike cluster managed by one or more administrative groups such as SREs, DevOps, DBAs, etc.&lt;/p&gt;

&lt;p&gt;And on the right we have downstream systems which ingest and analyze security events and log data for use by information security teams.&lt;/p&gt;

&lt;p&gt;The red arrows highlight where there is network connectivity between the Aerospike database and other systems or users as well as connectivity in between individual Aerospike nodes. This is where we need to apply network security.&lt;/p&gt;

&lt;h2&gt;
  
  
  Firewall Rules
&lt;/h2&gt;

&lt;p&gt;First, let's look at firewall rules adhering to the &lt;em&gt;principle of least privilege&lt;/em&gt; in which the firewall blocks all traffic and then rules are opened up to allow network access to only as needed.&lt;/p&gt;

&lt;p&gt;For an Aerospike cluster there are 4 types of network traffic that needs to be allowed.&lt;/p&gt;

&lt;p&gt;First, every Application node must be allowed to open up a TCP connection to every Aerospike node on the &lt;strong&gt;&lt;em&gt;service&lt;/em&gt;&lt;/strong&gt; port. This is port &lt;code&gt;3000&lt;/code&gt; by convention but all Aerospike network settings can be configured.&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%2Fhai4yag1bahe79lj1c41.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%2Fhai4yag1bahe79lj1c41.png" alt="Aerospike Service Connections" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The simple and more common way to setup these firewall rules is to allow the CIDR range for the &lt;em&gt;Application&lt;/em&gt; network to open TCP connections to the CIDR range for the &lt;em&gt;Aerospike&lt;/em&gt; network:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;td&gt;192.168.128.0/25&lt;/td&gt;
&lt;td&gt;10.0.1.0/25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;However, some security models might require each IP address to be &lt;em&gt;explicitly&lt;/em&gt; allowed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;td&gt;192.168.128.1&lt;/td&gt;
&lt;td&gt;10.0.1.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;td&gt;192.168.128.1&lt;/td&gt;
&lt;td&gt;10.0.1.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;td&gt;192.168.128.2&lt;/td&gt;
&lt;td&gt;10.0.1.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;td&gt;192.168.128.2&lt;/td&gt;
&lt;td&gt;10.0.1.2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;The next type of network traffic to allow are the &lt;strong&gt;&lt;em&gt;heartbeat&lt;/em&gt;&lt;/strong&gt; connections between each Aerospike node. This is the &lt;a href="https://www.aerospike.com/docs/operations/configure/network/heartbeat/" rel="noopener noreferrer"&gt;clustering protocol&lt;/a&gt; in mesh mode which allows the Aerospike nodes to form a cluster.&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%2F1z7vpxgdtahqgolg5q4q.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%2F1z7vpxgdtahqgolg5q4q.png" alt="Aerospike Heartbeat Connections" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every Aerospike node must be allowed to open a TCP connection to every other Aerospike node on the heartbeat port which is 3001 by convention.&lt;/p&gt;

&lt;p&gt;The common rule to allow the heartbeat connectivity in the Aerospike CIDR range:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3001&lt;/td&gt;
&lt;td&gt;10.0.1.0/25&lt;/td&gt;
&lt;td&gt;10.0.1.0/25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, the rules to allow explicit IP addresses for Aerospike nodes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;td&gt;10.0.1.1&lt;/td&gt;
&lt;td&gt;10.0.1.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;td&gt;10.0.1.2&lt;/td&gt;
&lt;td&gt;10.0.1.1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;The third type of network traffic to allow are the &lt;strong&gt;&lt;em&gt;fabric&lt;/em&gt;&lt;/strong&gt; connections between each Aerospike node. This connectivity allows data to transfer between the nodes for replication and "migrations" (redistribution of data).&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%2F1z7vpxgdtahqgolg5q4q.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%2F1z7vpxgdtahqgolg5q4q.png" alt="Aerospike Fabric Connections" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every Aerospike node must be allowed to open a TCP connection to every other Aerospike node on the fabric port which is 3002 by convention.&lt;/p&gt;

&lt;p&gt;The common rule to allow the fabric connectivity in the Aerospike CIDR block range:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3002&lt;/td&gt;
&lt;td&gt;10.0.1.0/25&lt;/td&gt;
&lt;td&gt;10.0.1.0/25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, the rules to allow explicit IP addresses for Aerospike nodes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3002&lt;/td&gt;
&lt;td&gt;10.0.1.1&lt;/td&gt;
&lt;td&gt;10.0.1.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3002&lt;/td&gt;
&lt;td&gt;10.0.1.2&lt;/td&gt;
&lt;td&gt;10.0.1.1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice that the fabric rules are identical to the heartbeat rules except for the port. If the configured ports are sequential then the rules for heartbeat and fabric can be combined for firewalls that allow specifying port ranges:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3001-3002&lt;/td&gt;
&lt;td&gt;10.0.1.0/25&lt;/td&gt;
&lt;td&gt;10.0.1.0/25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;The fourth and final type of network traffic is only applicable for deployments which are using &lt;a href="https://www.aerospike.com/docs/architecture/xdr.html" rel="noopener noreferrer"&gt;Cross Datacenter Replication (XDR)&lt;/a&gt; to replicate data between Aerospike clusters in different data centers or cloud regions.&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%2Fmoqskd4jex5pvtj411q9.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%2Fmoqskd4jex5pvtj411q9.png" alt="Aerospike XDR Connections" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;XDR traffic uses the same service connections that applications use. That means that every Aerospike node in the XDR source cluster must be allowed to open a TCP connection to every Aerospike node on the &lt;strong&gt;&lt;em&gt;service&lt;/em&gt;&lt;/strong&gt; port which is 3000 by convention.&lt;/p&gt;

&lt;p&gt;The rule to allow traffic from the XDR source cluster to the XDR destination cluster using CIDR blocks:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;td&gt;10.0.1.0/25&lt;/td&gt;
&lt;td&gt;172.16.0.0/25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, the rules to allow explicit IP addresses:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;td&gt;10.0.1.1&lt;/td&gt;
&lt;td&gt;172.16.0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;td&gt;10.0.1.1&lt;/td&gt;
&lt;td&gt;172.16.0.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;td&gt;10.0.1.2&lt;/td&gt;
&lt;td&gt;172.16.0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;td&gt;10.0.1.2&lt;/td&gt;
&lt;td&gt;172.16.0.2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice that the xdr rules are identical to the service rules except for the source being the XDR source instead of the Application nodes. The rules for service and xdr can be combined for firewalls that allow specifying multiple CIDR/IP ranges:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ALLOW&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;3000&lt;/td&gt;
&lt;td&gt;172.16.0.0/25,192.168.128.0/25&lt;/td&gt;
&lt;td&gt;10.0.1.0/25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Encryption in Transit (TLS)
&lt;/h2&gt;

&lt;p&gt;The second part of securing the network is about using TLS to encrypt data in transit and ensure connections are only established with trusted machines on the network.&lt;/p&gt;

&lt;h3&gt;
  
  
  TLS Certificates
&lt;/h3&gt;

&lt;p&gt;We just looked at the 4 types of network connectivity: &lt;strong&gt;&lt;em&gt;service&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;heartbeat&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;fabric&lt;/em&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;em&gt;XDR&lt;/em&gt;&lt;/strong&gt;. Aerospike can be configured to use TLS on each of those types of network connections independently.&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%2Fz5ootd7axq4afh532bhd.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%2Fz5ootd7axq4afh532bhd.png" alt="Aerospike TLS Encryption" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;em&gt;service&lt;/em&gt;&lt;/strong&gt; connections support standard or mutual authentication TLS also referred to as mTLS.&lt;/p&gt;

&lt;p&gt;Both modes encrypt the data in transit, however, with standard TLS, only the Aerospike nodes authenticate themselves to the application nodes. With mutual TLS, the Aerospike nodes authenticate themselves to the application nodes who also authenticate themselves to the Aerospike nodes. So it’s a 2-way authentication.&lt;/p&gt;

&lt;p&gt;A “bad actor” that found its way into the network somehow, could not pretend to be an application node nor pretend to be an Aerospike node without possessing the correct private key.&lt;/p&gt;




&lt;p&gt;When TLS is enabled on the &lt;strong&gt;&lt;em&gt;fabric&lt;/em&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;em&gt;heartbeat&lt;/em&gt;&lt;/strong&gt; connections, they will &lt;em&gt;always&lt;/em&gt; use what amounts to mutual authentication for those Aerospike-to-Aerospike connections. So once again, if a “bad actor” somehow breaches the private network, they could not pretend to be another Aerospike node in the cluster nor decrypt any of the data transferring between nodes without possessing the appropriate private key.&lt;/p&gt;




&lt;p&gt;If you recall, &lt;strong&gt;&lt;em&gt;XDR&lt;/em&gt;&lt;/strong&gt; connectivity is actually just using the service connections. So with XDR, the source cluster acts as the TLS clients, much like the application nodes, and the destination cluster acts as the TLS servers.&lt;/p&gt;




&lt;p&gt;Now, all of these &lt;em&gt;types of connections&lt;/em&gt; are generally configured to use the same server certificate as they are the same servers, however, they can technically be configured to have separate certificates.&lt;/p&gt;

&lt;p&gt;Additionally, every Aerospike node can be configured to use the same certificate, meaning the entire cluster shares that certificate, or every node can be set up with it’s own unique certificates.&lt;/p&gt;

&lt;p&gt;So that gives us three dimensions to work with; Standard vs. mutual TLS on the service connections, individual or shared certificates on each type of connection, and individual or shared certificates on each Aerospike server node.&lt;/p&gt;

&lt;p&gt;Obviously, standard TLS with a single cluster-wide certificate is the simplest in terms of setup and management complexity. And if you’ve spent much time dealing with certificate lifecycle management, one certificate certainly sounds more pleasant to manage than dozens, hundreds, or thousands. And indeed it is.&lt;/p&gt;

&lt;p&gt;But for organizations adopting more of a “zero trust” model, perhaps within environments dealing with highly sensitive data, on networks which the organization has deemed as untrusted such as the public cloud, unique certificates on each node may be required.&lt;/p&gt;

&lt;p&gt;However, most enterprise use cases will fall somewhere in between these two extremes and the flexibility of Aerospike’s TLS configuration will allow it to be tailored to the specific needs of the organization, the environment, and the use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  TLS Cipher Suites
&lt;/h3&gt;

&lt;p&gt;A cipher suite is a set of algorithms that are used in various phases during the TLS communication. The protocol allows for the client and server to negotiate which set of these algorithms both sides support.&lt;/p&gt;

&lt;p&gt;Every TLS connection I described in the previous section can be configured as to which cipher suites are allowed and in what priority.&lt;/p&gt;

&lt;p&gt;Without going too deep into the weeds about TLS cipher suites, let me just make two points about selecting TLS cipher suites to use with Aerospike Enterprise.&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%2F918jql2taf66y8p7ojf5.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%2F918jql2taf66y8p7ojf5.png" alt="Aerospike TLS Cipher Suites" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Point #1&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unlike a public, internet-facing application, many Aerospike deployments are done in environments where the organization is in control of both the client and the server. That means that compatibility with public clients like web browsers is not a factor and the list of allowed cipher suites can be narrowed down to just the more current algorithms which provide the best security and performance.&lt;/p&gt;

&lt;p&gt;At the time of this presentation, that is highly likely to mean a cipher suite using AES encryption, which has hardware acceleration built-in to modern CPUs, using Galois Counter Mode or GCM, which also typically out-performs previous block cipher modes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Point #2&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Aerospike uses OpenSSL and thus configuring the cipher suite uses the OpenSSL notation. This is recognizable by the use of hyphens as shown in the top line in the image. Other tools and libraries, such as Java, may use the IANA notation. This is recognizable by the use of underscores as shown in the second line here. This means that specifying the cipher suites in Aerospike configuration may use a different notation that other sources you may be referencing.&lt;/p&gt;

</description>
      <category>aerospike</category>
      <category>nosql</category>
      <category>security</category>
    </item>
    <item>
      <title>Serialized or Compressed Objects with Aerospike? Consider carefully.</title>
      <dc:creator>Micah Carrick</dc:creator>
      <pubDate>Fri, 11 Sep 2020 09:17:24 +0000</pubDate>
      <link>https://dev.to/aerospike/serialized-or-compressed-objects-with-aerospike-consider-carefully-133l</link>
      <guid>https://dev.to/aerospike/serialized-or-compressed-objects-with-aerospike-consider-carefully-133l</guid>
      <description>&lt;p&gt;&lt;em&gt;Make sure you consider these trade-offs before storing serialized or compressed blobs in Aerospike.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Serializing and compressing data client-side before storing it in a back-end database is a common pattern. I run into this frequently in my work with Aerospike customers, typically in the form of protocol buffers (protobuf) or gzipped JSON. After all, who doesn't want to reduce network bandwidth and storage?&lt;/p&gt;

&lt;p&gt;However, unless the use case is a dumb-simple get/put cache, you may be trading off some powerful Aerospike functionality for very little gain - if any.&lt;/p&gt;

&lt;p&gt;Some of the benefits of serializing and compressing objects client-side include the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interoperability&lt;/strong&gt;: Application developers can work in language-native constructs and exchange objects in a language-agnostic format. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Network bandwidth&lt;/strong&gt;: Getting and putting compressed objects that are compressed lower network bandwidth.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storage space&lt;/strong&gt;: Storing objects that are compressed usually use less storage space disk.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are all good things. However, Aerospike provides alternative mechanisms to achieve each of these benefits as well. &lt;a href="https://www.aerospike.com/docs/client/" rel="noopener noreferrer"&gt;Aerospike client libraries&lt;/a&gt; allow application developers to work in language-native constructs for Aerospike data, &lt;a href="https://www.aerospike.com/docs/guide/policies.html" rel="noopener noreferrer"&gt;client policies&lt;/a&gt; can enable compression on the network, and &lt;a href="https://www.aerospike.com/docs/operations/configure/namespace/storage/compression.html" rel="noopener noreferrer"&gt;storage compression&lt;/a&gt; can be enabled and tuned.&lt;/p&gt;

&lt;p&gt;Moreover, Aerospike will add some sensible logic and flexibility without any additional work in the applications.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When compression is enabled Aerospike won't store objects compressed if the result is actually &lt;em&gt;larger&lt;/em&gt; when compressed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Compression comes at the cost of CPU. Applications can choose to compress data on the network on a &lt;em&gt;per-transaction&lt;/em&gt; basis and storage compression algorithm/level can be configured on a &lt;em&gt;per-namespace&lt;/em&gt; basis. This allows optimizing to get the most "bang for your buck" per use case.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you may already be thinking: "Well, gosh, maybe I don't need to serialize all my objects client-side". But the real trade-off to consider is not about the fact that you get parity with Aerospike out-of-the-box, it's about &lt;em&gt;what you lose&lt;/em&gt; when storing serialized and/or compressed blobs in Aerospike.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You will lose the ability to do &lt;a href="https://www.aerospike.com/docs/guide/predicate.html" rel="noopener noreferrer"&gt;Predicate Filters&lt;/a&gt; on &lt;a href="https://www.aerospike.com/docs/guide/query.html" rel="noopener noreferrer"&gt;queries&lt;/a&gt; and &lt;a href="https://www.aerospike.com/docs/guide/scan.html" rel="noopener noreferrer"&gt;scans&lt;/a&gt; against the data in the blob&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will lose the ability to leverage &lt;a href="https://www.aerospike.com/docs/guide/bitwise.html" rel="noopener noreferrer"&gt;Bitwise Operations&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will lose the ability to use the feature-rich Complex Data Type (CDT) API on &lt;a href="https://www.aerospike.com/docs/guide/cdt-list-ops.html" rel="noopener noreferrer"&gt;Lists&lt;/a&gt; and &lt;a href="https://www.aerospike.com/docs/guide/cdt-map-ops.html" rel="noopener noreferrer"&gt;Maps&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now those are some incredibly useful features - especially those CDT operations. Unless you know you only want a dumb get/put cache you don't want to miss out on those.&lt;/p&gt;

&lt;p&gt;But you're a geek... I'm a geek... so let's see this in action with some quick 'n dirty Python.&lt;/p&gt;




&lt;h2&gt;
  
  
  Serialized/Compressed Blobs vs Aerospike CDT Performance
&lt;/h2&gt;

&lt;p&gt;As an example, assume a use case which rolls up all purchase transactions by day and stores them in records split by month and account number using a composite key like: &lt;code&gt;monthly:&amp;lt;YYYYMM&amp;gt;:&amp;lt;Account ID&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In Python, each record can be represented as standard dictionary with nested lists and dictionaries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'monthly:201901:00001' {
  'acct': '00001',
  'loc': 1,
  'txns': {
    '20190101': [
      {
        'txn': 1,
        'ts': 50607338,
        'sku': 5631,
        'cid': "GFOBVQPRCZVT",
        'amt': 873300,
        'qty': 23,
        'code': 'USD'
      },
      { ... }
    ]
    '20190102': [ ... ]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;txns&lt;/code&gt; key contains a dictionary where each key in that dictionary is the day of the month in &lt;code&gt;YYMMDD&lt;/code&gt; format and the value is a list of every transaction for that day.&lt;/p&gt;

&lt;p&gt;In order to highlight the pros and cons of serialization/compression client-side vs. using Aerospike's built-in features, two Aerospike namespaces are setup.&lt;/p&gt;

&lt;p&gt;The first namespace &lt;code&gt;ns1&lt;/code&gt; uses a file storage engine without any compression enabled. It will be used to store the records as blobs that have been serialized as JSON and compressed with zlib level 6 (default) in the Python code.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;namespace&lt;/code&gt; stanza in the &lt;code&gt;aerospike.conf&lt;/code&gt; file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace ns1 {
    replication-factor 1
    memory-size 2G

    storage-engine device {
        file /opt/aerospike/data/ns1.dat
        filesize 100M
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second namespace &lt;code&gt;ns2&lt;/code&gt; uses a file storage engine with ZStandard compression level 1 (least amount of compression, best performance). It will be used to store the records as Aerospike CDTs.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;namespace&lt;/code&gt; stanza in the &lt;code&gt;aerospike.conf&lt;/code&gt; file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace ns2 {
    replication-factor 1
    memory-size 2G

    storage-engine device {
    file /opt/aerospike/data/ns2.dat
    filesize 100M
    compression zstd
            compression-level 1
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the Python script called &lt;code&gt;generate-data.py&lt;/code&gt;, dummy data is generated using the above data model and loaded into each of the two namespaces. It generates 2 years of historic transaction data for 10 accounts each doing 250 transactions per day.&lt;/p&gt;

&lt;p&gt;Looking at just the section of &lt;code&gt;generate-data.py&lt;/code&gt; that loads data into the two namespaces, the "blob" version first converts the Python object to JSON and then compresses the JSON string using zlib and then writes the record to &lt;code&gt;ns1&lt;/code&gt; namespace. The "cdt" version just writes the Python object as-is to the &lt;code&gt;ns2&lt;/code&gt; namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# write each record
&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;object_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;blob&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;record_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;zlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;zlib_level&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;object_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cdt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;record_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;

    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;set_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;exists&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aerospike&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POLICY_EXISTS_CREATE_OR_REPLACE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After &lt;code&gt;generate-data.py&lt;/code&gt;  loads the dummy objects into each of the Aerospike namespaces it outputs some statistics about that namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python3 generate-data.py 
Aerospike:          127.0.0.1:3000 ns1.example
Run time:           9.354 seconds
Object type:        blob
Object count:       240
Avg object size:    217.0 KiB
Compression ratio:  -
---
Aerospike:          127.0.0.1:3000 ns2.example
Run time:           5.610 seconds
Object type:        cdt
Object count:       240
Avg object size:    182.5 KiB
Compression ratio:  0.349
---
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So right away it is clear that using Aerospike native CDTs with compression enabled results in smaller objects (better storage compression) and loaded the data faster.&lt;/p&gt;

&lt;p&gt;Some of this can be explained by the fact that (a) when Aerospike compresses the data it is using ZStandard compression instead of zlib which was used in the Python code and (b) Aerospike is built with a very fast, statically typed, compiled language (C lang) and our Python code is a slower, dynamically typed language running on an interpreter. So it is certainly not apples-to-apples.&lt;/p&gt;

&lt;p&gt;However, the two key takeaways here, from the application development perspective, are that the Aerospike compression is essentially free and that you work in your native language types.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case: Correct Data with Background Read/Write Scan
&lt;/h2&gt;

&lt;p&gt;To illustrate the value of being able to leverage advanced Aerospike features that are not available when doing client-side serialization/compression, let's take a look at a data correction use case. Suppose that there was a bug in the application that resulted in an incorrect value for the location (&lt;code&gt;loc&lt;/code&gt;) for just one account (&lt;code&gt;acct&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If the records are serialized and compressed client-side, application code would need to read every record back over the network, into the application RAM, deserialize and decompress it, make the correction, and then write the entire record back over the network.&lt;/p&gt;

&lt;p&gt;However, if using Aerospike CDTs with server-side compression, the application can initiate a &lt;a href="https://www.aerospike.com/docs/guide/scan.html" rel="noopener noreferrer"&gt;background read/write scan&lt;/a&gt; with a &lt;a href="https://www.aerospike.com/docs/guide/predicate.html" rel="noopener noreferrer"&gt;predicate filter&lt;/a&gt; to do the work entirely on the Aerospike nodes.&lt;/p&gt;

&lt;p&gt;An example of this is illustrated in the Python script &lt;code&gt;correct-data.py&lt;/code&gt;. This script operates on the same data that was generated with &lt;code&gt;generate-data.py&lt;/code&gt; above.&lt;/p&gt;

&lt;p&gt;First, a predicate filter is setup which will filter records to only those that have an account ID (&lt;code&gt;acct&lt;/code&gt;) of &lt;code&gt;00007&lt;/code&gt; &lt;strong&gt;AND&lt;/strong&gt; a current location ID (&lt;code&gt;loc&lt;/code&gt;) value of &lt;code&gt;5&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;account_to_correct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00007&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;incorrect_location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="n"&gt;predicate_expressions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;

    &lt;span class="c1"&gt;# push expressions to filter by loc=5
&lt;/span&gt;    &lt;span class="n"&gt;predexp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer_bin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;loc&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;predexp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;incorrect_location&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;predexp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer_equal&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

    &lt;span class="c1"&gt;# push expression to filter by acct=00007
&lt;/span&gt;    &lt;span class="n"&gt;predexp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string_bin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;acct&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;predexp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account_to_correct&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;predexp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string_equal&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

    &lt;span class="c1"&gt;# filter by the `loc` AND `acct` expressions
&lt;/span&gt;    &lt;span class="n"&gt;predexp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predexp_and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;predexp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;predicate_expressions&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, a background scan is sent to each Aerospike node using the above predicate expressions to filter the scan results and passing an array of write operations to perform on each resulting record. In this case, the write ops contains just one operation to update the location ID (&lt;code&gt;loc&lt;/code&gt;) to &lt;code&gt;2&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;correct_location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="c1"&gt;# Do a background scan, which runs server-side, to update the records that
# match the predicate expression with the correct value for 'loc'.
&lt;/span&gt;&lt;span class="n"&gt;ops&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;operations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;loc&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correct_location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;bgscan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;set_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;bgscan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_ops&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;scan_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bgscan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_background&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Running background read/write scan. ID: {}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scan_id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# Wait for the background scan to complete.
&lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;job_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scan_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aerospike&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JOB_SCAN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;aerospike&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JOB_STATUS_INPROGRESS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's that? You're worried about that background read/write scan impacting performance? No worries, Aerospike has that covered by allowing you to throttle the records per second using the &lt;a href="https://www.aerospike.com/docs/reference/configuration/#background-scan-max-rps" rel="noopener noreferrer"&gt;background-scan-max-rps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Consider all the opportunities to optimize cost and performance by sending lightweight binary &lt;em&gt;operations&lt;/em&gt; to the Aerospike database nodes rather than passing ~200k objects back and forth to be processed client-side. Think about how much money you could save! You'll be a hero!&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MicahCarrick/micahcarrick-posts/tree/master/aerospike-serialize-vs-cdt" rel="noopener noreferrer"&gt;View complete source code on Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aerospike</category>
      <category>nosql</category>
      <category>protobuf</category>
    </item>
  </channel>
</rss>
