<?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: Sarma</title>
    <description>The latest articles on DEV Community by Sarma (@seetamraju).</description>
    <link>https://dev.to/seetamraju</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%2F207619%2F11bf8715-4ba2-437e-9783-372d1ce434d6.png</url>
      <title>DEV Community: Sarma</title>
      <link>https://dev.to/seetamraju</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/seetamraju"/>
    <language>en</language>
    <item>
      <title>Vector-Database: Qdrant-cluster on ECS-Fargate</title>
      <dc:creator>Sarma</dc:creator>
      <pubDate>Wed, 31 Dec 2025 03:36:08 +0000</pubDate>
      <link>https://dev.to/seetamraju/vector-database-qdrant-cluster-on-ecs-fargate-2jak</link>
      <guid>https://dev.to/seetamraju/vector-database-qdrant-cluster-on-ecs-fargate-2jak</guid>
      <description>&lt;h2&gt;
  
  
  When do you need this?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;When SaaS is not viable due to compliance-reasons or for network-performance.&lt;/li&gt;
&lt;li&gt;When Vector-DB should be in the same aws-account as the rest of the solution&lt;/li&gt;
&lt;li&gt;When No kubernetes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions?  See the &lt;a href="https://dev.to/seetamraju/design-vector-database-qdrant-cluster-38ab"&gt;article # 2&lt;/a&gt; which has great detail, as to why certain things &lt;em&gt;HAVE&lt;/em&gt; to be that way!  This article (# 1) just focuses on cdk-snippets to quickly get started (for those very familiar with CDK).&lt;/p&gt;

&lt;p&gt;All of this (CDK, ECS &amp;amp; Clusters) is too much to handle?&lt;br&gt;
Seek professional-services from Qdrant.tech; If not, me/my current-employer (an AWS-partner).&lt;/p&gt;
&lt;h2&gt;
  
  
  Short Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Run a 4/or/6-node Qdrant-cluster on ECS, across 2-or-3 AZs&lt;/li&gt;
&lt;li&gt;Keep it very simple, by making AWS take care of almost everything re: availability, uptime, AZ-balancing.&lt;/li&gt;
&lt;li&gt;No matter how many failed nodes are replaced, use a single "endpoint" (to connect to the Qdrant-cluster).&lt;/li&gt;
&lt;li&gt;No EBS.  Rely on EFS for "native" Snapshots, for protecting data.&lt;/li&gt;
&lt;li&gt;Securing access to Qdrant-Dashboard via ALB + Qdrant-native API-Key.&lt;/li&gt;
&lt;/ul&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%2Fxc1d5qbwgs1l0ip7fngb.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%2Fxc1d5qbwgs1l0ip7fngb.png" alt=" " width="736" height="937"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  how do I .. ?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; A &lt;a href="https://dev.to/seetamraju/design-vector-database-qdrant-cluster-38ab"&gt;article # 2&lt;/a&gt; has FULL DETAILs on the Critical-Design &amp;amp; Key-requirements that influenced/constrained/forced the final implementation.&lt;/li&gt;
&lt;li&gt; A &lt;a href="https://dev.to/seetamraju/snapshots-data-restore-vector-database-qdrant-cluster-33gj"&gt;article # 3&lt;/a&gt; re: Snapshots.&lt;/li&gt;
&lt;li&gt; A separate GitLab-repo contains the full CDK-Construct.&lt;/li&gt;
&lt;li&gt; Assumption: You'll OK to CUSTOM-build the Qdrant Container-IMAGE (using a custom Dockerfile) using Qdrant's github.  &lt;a href="https://dev.to/seetamraju/vector-database-qdrant-cluster-dockerfile-4lal"&gt;article # 4&lt;/a&gt; for a sensible/defensible &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Below is the key "lego-blocks" to get started, for CDK-experts.&lt;/p&gt;

&lt;p&gt;Questions?  See the &lt;a href="https://dev.to/seetamraju/design-vector-database-qdrant-cluster-38ab"&gt;article # 2&lt;/a&gt; which has great detail, as to why certain things &lt;em&gt;HAVE&lt;/em&gt; to be that way!&lt;/p&gt;
&lt;h2&gt;
  
  
  Key variables
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;bashScriptFromArticle4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;..&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;inside&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;docker__to_bash&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="p"&gt;..;&lt;/span&gt;

&lt;span class="nx"&gt;ecsClusterName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;..&lt;/span&gt; &lt;span class="p"&gt;..&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;cpuArchStr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;fargateContainerName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;..&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;cpuArchStr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;fargateServiceName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ECSSvc-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;fargateContainerName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;taskDefContainerName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cont-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;fargateContainerName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;domainName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ecsClusterName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.local&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;qdrantFQDN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qdrant-cluster.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;domainName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;nativeApiKeySecret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws_secretsmanager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;..&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// excludeCharacters = "~`@#$%^&amp;amp;*+={}[]()|\\:;'\"”&amp;lt;&amp;gt;,/? ";&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fargate Task Definition
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FargateTaskDefinition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;..&lt;/span&gt; &lt;span class="p"&gt;..&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fargateContainerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//// Equivalent to FargateTaskName/taskname&lt;/span&gt;
    &lt;span class="nx"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ECS_FARGATE_CPU_SIZING&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;memoryLimitMiB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ECS_FARGATE_MEMORY_SIZING_GB&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;enableFaultInjection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;basicProps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tier&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;ephemeralStorageGiB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// ValidationError: Ephemeral storage size must be between 21GiB and 200GiB&lt;/span&gt;
    &lt;span class="nx"&gt;runtimePlatform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;cpuArchitecture&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ecsCpuArchitecture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operatingSystemFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OperatingSystemFamily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LINUX&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fargateContainerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;//// This name -MUST- referenced in the aws_ecs.FargateService.&lt;/span&gt;
        &lt;span class="na"&gt;efsVolumeConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;fileSystemId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;efsFileSystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileSystemId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;rootDirectory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;transitEncryption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ENABLED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;authorizationConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;accessPointId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;efsAccessPoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessPointId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ENABLED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&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;h2&gt;
  
  
  Fargate Service
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FargateService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;..&lt;/span&gt; &lt;span class="p"&gt;..,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myECSCluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fargateServiceName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;taskDefinition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;qdrantTaskDefinition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;desiredCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;desiredCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;minHealthyPercent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Allow the single task to be stopped during deployment&lt;/span&gt;
    &lt;span class="na"&gt;maxHealthyPercent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Allow 1 new task to start before stopping the old one&lt;/span&gt;
    &lt;span class="na"&gt;vpcSubnets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;onePerAz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;subnetType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SubnetType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRIVATE_WITH_EGRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;availabilityZones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;availabilityZones&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;securityGroups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;assignPublicIp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;enableExecuteCommand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// Enable ECS Exec for debugging&lt;/span&gt;
    &lt;span class="na"&gt;propagateTags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PropagatedTagSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SERVICE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;availabilityZoneRebalancing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AvailabilityZoneRebalancing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ENABLED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;circuitBreaker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rollback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;// Enable deployment circuit breaker&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Container for Fargate-Task
&lt;/h2&gt;

&lt;p&gt;Refer to &lt;a href="https://dev.to/seetamraju/design-vector-database-qdrant-cluster-38ab"&gt;article 4&lt;/a&gt; for how to &lt;code&gt;docker build&lt;/code&gt; the Docker-image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;containerImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ContainerImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEcrRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ecrRepo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;qdrantImageTag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//// Add Container to Task Definition&lt;/span&gt;
&lt;span class="nx"&gt;dockerLabels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app.cluster&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ecsClusterName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fargateServiceName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app.task-definition&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fargateContainerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// must match family's name of task-definition&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vector-database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app.version&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdkContextBuildQdrantImageTag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app.environment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;basicProps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;portMappings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6333&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TCP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qdrant-rest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;appProtocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AppProtocol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6334&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TCP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qdrant-grpc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;appProtocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AppProtocol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6335&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TCP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qdrant-cluster&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;appProtocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AppProtocol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nx"&gt;containerEnvironmentVariables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// runtime environment-variables used by Qdrant-VectorDB docker-container&lt;/span&gt;
    &lt;span class="na"&gt;QDRANT__CLUSTER__ENABLED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;QDRANT__SERVICE__API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nativeApiKeySecret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secretValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unsafeUnwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;qdrantPrimaryNodeTaskDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;QdrantContainer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;containerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;taskDefContainerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;containerImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1000:1000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/bin/sh&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bashScriptFromArticle4&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;constantsCdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ECS_FARGATE_CPU_SIZING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;memoryLimitMiB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ECS_FARGATE_MEMORY_SIZING_GB&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// in MB&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;containerEnvironmentVariables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dockerLabels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;dockerLabels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app.instance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary-node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;portMappings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;essential&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;containerLogs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;healthCheck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;commonInsideContainerHealthCheck&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;h2&gt;
  
  
  SecurityGroup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SecurityGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;cdkScope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;QdrantSecurityGroupForFargate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;securityGroupName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ecsClusterName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-SvcInbound-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;fargateContainerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`For FARGATE-service - inbound 6333,6334 only, NO outbound. Cluster: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ecsClusterName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, Container: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fargateContainerName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;allowAllOutbound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Deny/Allow -- Explicitly all outbound traffic&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;//// Add inbound rules for Qdrant ports&lt;/span&gt;
&lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addIngressRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ipv4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpcCidrBlock&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6333&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Allow inbound traffic on port 6333 (Qdrant REST API)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//// Add outbound HTTPS for ECR image pulling&lt;/span&gt;
&lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEgressRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;anyIpv4&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Allow outbound HTTPS for ECR image pulling, whether or NOT using VPC Endpoints&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//// Cluster-replication traffic; REF: https://qdrant.tech/documentation/guides/distributed_deployment/#enabling-distributed-mode-in-self-hosted-qdrant&lt;/span&gt;
&lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addIngressRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6334&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Allow INTRA-CLUSTER Replication traffic on port 6334 (Qdrant gRPC API)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEgressRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6334&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Allow INTRA-CLUSTER Replication traffic on port 6334 (Qdrant gRPC API)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addIngressRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6335&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Allow INTRA-CLUSTER Replication traffic on port 6335 (Qdrant Cluster-Replication traffic)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEgressRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6335&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Allow INTRA-CLUSTER Replication traffic on port 6335 (Qdrant Cluster-Replication traffic)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//// Allow Fargate tasks to access EFS&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;efsSecurityGroup&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;efsSecurityGroups&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;efsSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addIngressRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2049&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Allow NFS access from Fargate tasks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;fargateSvcSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEgressRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;efsSecurityGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aws_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2049&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Allow NFS access to EFS&lt;/span&gt;&lt;span class="dl"&gt;'&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;h2&gt;
  
  
  Other related articles
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Get-Started &lt;a href="https://dev.to/seetamraju/vector-database-qdrant-cluster-on-ecs-fargate-2jak"&gt;article # 1&lt;/a&gt; - this one.&lt;/li&gt;
&lt;li&gt; A &lt;a href="https://dev.to/seetamraju/design-vector-database-qdrant-cluster-38ab"&gt;article # 2&lt;/a&gt; has FULL DETAILs on the Critical-Design &amp;amp; Key-requirements that influenced/constrained/forced the final implementation.&lt;/li&gt;
&lt;li&gt; A &lt;a href="https://dev.to/seetamraju/snapshots-data-restore-vector-database-qdrant-cluster-33gj"&gt;article # 3&lt;/a&gt; re: Snapshots.&lt;/li&gt;
&lt;li&gt; A separate GitLab-repo contains the full CDK-Construct.&lt;/li&gt;
&lt;li&gt; Assumption: You'll OK to CUSTOM-build the Qdrant Container-IMAGE (using a custom Dockerfile) using Qdrant's github.  &lt;a href="https://dev.to/seetamraju/vector-database-qdrant-cluster-dockerfile-4lal"&gt;article # 4&lt;/a&gt; for a sensible/defensible &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;End.&lt;/p&gt;

</description>
      <category>vectordatabase</category>
      <category>ecs</category>
      <category>fargate</category>
      <category>qdrant</category>
    </item>
    <item>
      <title>Vector-Database: Qdrant-cluster (Fargate) - Dockerfile</title>
      <dc:creator>Sarma</dc:creator>
      <pubDate>Wed, 31 Dec 2025 03:33:55 +0000</pubDate>
      <link>https://dev.to/seetamraju/vector-database-qdrant-cluster-dockerfile-4lal</link>
      <guid>https://dev.to/seetamraju/vector-database-qdrant-cluster-dockerfile-4lal</guid>
      <description>&lt;p&gt;This is the last of the article set.  Go to "Get-Started" &lt;a href="https://dev.to/seetamraju/vector-database-qdrant-cluster-on-ecs-fargate-2jak"&gt;article # 1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why on earth did I chose to create my own Dockerfile?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Technically, yes its "my own".  But, its Not a "new" Dockerfile.  I just slashed-n-slashed-n-slashed Qdrant's official &lt;code&gt;Dockerfile&lt;/code&gt; until I got it to this.  It can be simplified further by removing 'GPU' support.&lt;/li&gt;
&lt;li&gt; I initially tried using Qdrant's as-is &lt;code&gt;Dockerfile&lt;/code&gt;, but that made the CDK-design extremely complicated.  I then added a small "starter" bash-script for the container-image, and voila! a simple CDK !&lt;/li&gt;
&lt;li&gt; There was &lt;strong&gt;NO way&lt;/strong&gt; I was trusting all the 3rd-party images and 3rd-party packages that Qdrant was installing in their Dockerfile.&lt;/li&gt;
&lt;li&gt; My Dockerfile just relies on &lt;code&gt;debian-slim&lt;/code&gt;, &lt;code&gt;cargo-chef&lt;/code&gt; and &lt;code&gt;mold&lt;/code&gt;.  That is it.  And, adds &lt;code&gt;nslookup&lt;/code&gt; only to simplify the cluster deployment-design.&lt;/li&gt;
&lt;li&gt; All my base-images HAD to be Verified images from &lt;code&gt;public.ecr.aws&lt;/code&gt;.  A religious decision.  Dockerhub-images over my dead-body.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What if Qdrant's official docker-image is mandatory?
&lt;/h3&gt;

&lt;p&gt;Perhaps your enterprise mandates the use of Qdrant's official ready-to-use Docker-images, rather than using the &lt;code&gt;Dockerfile&lt;/code&gt; from a suspicious no-name guy (ahem. me).&lt;/p&gt;

&lt;p&gt;OPTION 1: Use Qdrant's official as a "base" image, and incorporate the (following) bash-script into a new "derivative" container-image, and use that bash-script as the "entrypoint".  You also need to ensure &lt;code&gt;nslookup&lt;/code&gt; cli-command is available in your new image (the bash-script relies on it).&lt;/p&gt;

&lt;p&gt;OPTION 2: You'll need 3 Fargate Services (to ensure 2 shards that are replicated across 2-or-3 AZs).  Details are out of scope of this article-set. YES, I got this working too initially, but the COMPLEX set of aws-resources was un-maintainable. That led me to the DRASTICALLY-simplified design of just 1 Fargate-Service with just 1 Fargate-Task with just 1 Container (which is what this set of articles is about).&lt;/p&gt;

&lt;h2&gt;
  
  
  HOW-TO
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Copy the bash-script (below) into the Qdrant git-cloned folder.  FYI: It must be the "entrypoint" for the running container.&lt;/li&gt;
&lt;li&gt; Also, copy the CUSTOM &lt;code&gt;Dockerfile&lt;/code&gt; below into the Qdrant git-cloned folder.&lt;/li&gt;
&lt;li&gt; &lt;code&gt;docker build&lt;/code&gt; the container-image.&lt;/li&gt;
&lt;li&gt; Store container-image in an ECR-Repo.  Tag it appropriately.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Explaining the "entrypoint" Bash-script
&lt;/h2&gt;

&lt;p&gt;This bash-script does a DNS lookup of &lt;code&gt;qdrant-cluster.my-ecs-cluster.local&lt;/code&gt; (stored as the value of bash-variable &lt;code&gt;QdrantClusterFQDN&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;if the lookup returns no records, then container should run:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;./qdrant --bootstrap http://${QdrantClusterFQDN}:6335&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;else if 1+ record(s) is/are returned, then container should run:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;./qdrant --uri http://${QdrantClusterFQDN}:6335&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;else, for all other scenarios of the &lt;code&gt;nslookup&lt;/code&gt; cmd, dump the response of that command and print an error message&lt;/p&gt;

&lt;p&gt;Could Qdrant-corp have taken care of this simple logic within their binary?  It would make cluster-setup easier (but then, their livelihood is gone).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;NOTE: Per official documentation, the 1st node must be started as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;./qdrant --bootstrap  http://${qdrantPrimaryFQDN}:6335&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Except for the very-1st node (that uses the above cmd), all other "nodes/Containers" must be started as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;./qdrant --uri  http://${qdrantPrimaryFQDN}:6335&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That is, &lt;code&gt;--uri&lt;/code&gt; replaces &lt;del&gt;&lt;code&gt;--bootstrap&lt;/code&gt;&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;A big-problem happens when the 1st node itself fails and we need a replacement node in its place, in an automated manner!  If the replacement-node uses &lt;code&gt;--bootstrap&lt;/code&gt; (by definition/configuration), hell breaks loose. All such exceptional scenarios are well handled by the script.&lt;/p&gt;




&lt;h2&gt;
  
  
  bash-script
&lt;/h2&gt;

&lt;p&gt;Note: replace &lt;code&gt;my-ecs-cluster&lt;/code&gt; with the name of YOUR ecs-cluster.&lt;br&gt;
Note: Following is for &lt;code&gt;arm64&lt;/code&gt; with/without Nvidia-Gpu.&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/bash -f&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nv"&gt;MyClusterName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;my-ecs-cluster&lt;span class="s2"&gt;"
QdrantClusterFQDN="&lt;/span&gt;qdrant-cluster.&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MyClusterName&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.local&lt;span class="s2"&gt;"

# Install required DNS tools
echo "&lt;/span&gt;Installing DNS utilities...&lt;span class="s2"&gt;"
apt-get update &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
apt-get install -y --no-install-recommends dnsutils &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
apt-get autoremove -y &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
apt-get autoclean &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
rm -rf /var/lib/apt/lists/* /var/cache/apt/* /tmp/* /var/tmp/*

echo "&lt;/span&gt;Performing DNS lookup &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;QdrantClusterFQDN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;...&lt;span class="s2"&gt;"
if DNS_RESULT=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dig +short &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$QdrantClusterFQDN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; A 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; [ -n "&lt;/span&gt;&lt;span class="nv"&gt;$DNS_RESULT&lt;/span&gt;&lt;span class="s2"&gt;" ]; then
    RECORD_COUNT=&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;"&lt;/span&gt;&lt;span class="nv"&gt;$DNS_RESULT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-cE&lt;/span&gt; &lt;span class="s1"&gt;'^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;0&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;

    if [ "&lt;/span&gt;&lt;span class="nv"&gt;$RECORD_COUNT&lt;/span&gt;&lt;span class="s2"&gt;" -eq 0 ]; then
        echo "&lt;/span&gt;No valid IP records found. Bootstrapping totally-new Qdrant cluster...&lt;span class="s2"&gt;"
        exec ./qdrant --bootstrap "&lt;/span&gt;http://&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;QdrantClusterFQDN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:6335&lt;span class="s2"&gt;"
    else
        echo "&lt;/span&gt;Found &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RECORD_COUNT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; DNS record&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; Joining existing cluster...&lt;span class="s2"&gt;"
        exec ./qdrant --uri "&lt;/span&gt;http://&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;QdrantClusterFQDN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:6335&lt;span class="s2"&gt;"
    fi
else
    echo "&lt;/span&gt;dig &lt;span class="nb"&gt;command &lt;/span&gt;failed or returned empty result. Full output:&lt;span class="s2"&gt;"
    dig "&lt;/span&gt;&lt;span class="nv"&gt;$QdrantClusterFQDN&lt;/span&gt;&lt;span class="s2"&gt;" A
    echo "&lt;/span&gt;FATAL-Error Not able to determine &lt;span class="k"&gt;if &lt;/span&gt;this Qdrant-node is part of a cluster or not&lt;span class="s2"&gt;"
    exit 1
fi
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Dockerfile
&lt;/h2&gt;

&lt;p&gt;For &lt;code&gt;arm64&lt;/code&gt; cpu-architecture.&lt;/p&gt;

&lt;p&gt;Warning: Using an &lt;code&gt;XLARGE&lt;/code&gt; Codebuild-instance (32cpus, 128GB RAM), it still took 20 minutes to build the image !!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enable GPU support.&lt;/span&gt;
&lt;span class="c"&gt;# This option can be set to `nvidia` or `amd` to enable GPU support.&lt;/span&gt;
&lt;span class="c"&gt;# This option is defined here because it is used in `FROM` instructions.&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GPU&lt;/span&gt;

&lt;span class="c"&gt;# Use AWS ECR Public Gallery's official Rust image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;public.ecr.aws/docker/library/rust:1.90-bookworm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;

&lt;span class="c"&gt;# Install cargo-chef manually for better security control&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;cargo &lt;span class="nb"&gt;install &lt;/span&gt;cargo-chef &lt;span class="nt"&gt;--locked&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;planner&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /qdrant&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;cargo chef prepare &lt;span class="nt"&gt;--recipe-path&lt;/span&gt; recipe.json

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /qdrant&lt;/span&gt;

&lt;span class="c"&gt;# Install build dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        clang &lt;span class="se"&gt;\
&lt;/span&gt;        lld &lt;span class="se"&gt;\
&lt;/span&gt;        cmake &lt;span class="se"&gt;\
&lt;/span&gt;        protobuf-compiler &lt;span class="se"&gt;\
&lt;/span&gt;        libprotobuf-dev &lt;span class="se"&gt;\
&lt;/span&gt;        protobuf-compiler-grpc &lt;span class="se"&gt;\
&lt;/span&gt;        jq &lt;span class="se"&gt;\
&lt;/span&gt;        pkg-config &lt;span class="se"&gt;\
&lt;/span&gt;        gcc &lt;span class="se"&gt;\
&lt;/span&gt;        g++ &lt;span class="se"&gt;\
&lt;/span&gt;        libc6-dev &lt;span class="se"&gt;\
&lt;/span&gt;        libunwind-dev &lt;span class="se"&gt;\
&lt;/span&gt;        curl &lt;span class="se"&gt;\
&lt;/span&gt;        ca-certificates &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get clean &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt; /var/cache/debconf/&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; rustup component add rustfmt

&lt;span class="c"&gt;# `ARG`/`ENV` pair is a workaround for `docker build` backward-compatibility.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# https://github.com/docker/buildx/issues/510&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; BUILDPLATFORM&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; BUILDPLATFORM=${BUILDPLATFORM:-linux/arm64}&lt;/span&gt;

&lt;span class="c"&gt;# Pin mold version and verify checksum for Cpu-Architecture&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; MOLD_VERSION=2.36.0&lt;/span&gt;
&lt;span class="c"&gt;# ARG MOLD_SHA256_ARM64="4b20b3fac90ad3f5b5d4c0b65a6e3f4b4f10c6f7f1a8f9a8b1b1b1b1b1b1b1b1"&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /opt/mold &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;cd&lt;/span&gt; /opt/mold &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nv"&gt;TARBALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mold-&lt;/span&gt;&lt;span class="nv"&gt;$MOLD_VERSION&lt;/span&gt;&lt;span class="s2"&gt;-aarch64-linux.tar.gz"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    curl &lt;span class="nt"&gt;-sSLO&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/rui314/mold/releases/download/v&lt;/span&gt;&lt;span class="nv"&gt;$MOLD_VERSION&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$TARBALL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARBALL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--strip-components&lt;/span&gt; 1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARBALL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# echo "$MOLD_SHA256_ARM64  $TARBALL" | sha256sum -c - &amp;amp;&amp;amp; \&lt;/span&gt;


&lt;span class="c"&gt;# `ARG`/`ENV` pair is a workaround for `docker build` backward-compatibility.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# https://github.com/docker/buildx/issues/510&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; TARGETPLATFORM&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; TARGETPLATFORM=${TARGETPLATFORM:-linux/arm64}&lt;/span&gt;

&lt;span class="c"&gt;# Set protobuf environment variables&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PROTOC=/usr/bin/protoc&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PROTOC_INCLUDE=/usr/include&lt;/span&gt;

&lt;span class="c"&gt;# Select Cargo profile (e.g., `release`, `dev` or `ci`)&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; PROFILE=release&lt;/span&gt;

&lt;span class="c"&gt;# Enable crate features&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; FEATURES&lt;/span&gt;

&lt;span class="c"&gt;# Pass custom `RUSTFLAGS` (e.g., `--cfg tokio_unstable` to enable Tokio tracing/`tokio-console`)&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; RUSTFLAGS&lt;/span&gt;

&lt;span class="c"&gt;# Select linker (e.g., `mold`, `lld` or an empty string for the default linker)&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; LINKER=mold&lt;/span&gt;

&lt;span class="c"&gt;# Enable GPU support&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GPU&lt;/span&gt;

&lt;span class="c"&gt;# Download and extract web UI&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; tools/ tools/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; docs/ docs/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /static &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;STATIC_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/static ./tools/sync-web-ui.sh

&lt;span class="c"&gt;# Cook dependencies&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=planner /qdrant/recipe.json recipe.json&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;:/opt/mold/bin"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nv"&gt;PROTOC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/bin/protoc &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nv"&gt;PROTOC_INCLUDE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/include &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nv"&gt;CARGO_BUILD_JOBS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32 &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nv"&gt;RUSTFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LINKER&lt;/span&gt;:+-C&lt;span class="p"&gt; link-arg=-fuse-ld=&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nv"&gt;$LINKER&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$RUSTFLAGS&lt;/span&gt;&lt;span class="s2"&gt; -C codegen-units=16"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    cargo chef cook &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FEATURES&lt;/span&gt;:+--features&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;$FEATURES&lt;/span&gt; &lt;span class="nt"&gt;--features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stacktrace &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GPU&lt;/span&gt;:+--features&lt;span class="p"&gt;=gpu&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--recipe-path&lt;/span&gt; recipe.json

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="c"&gt;# Include git commit into Qdrant binary during build&lt;/span&gt;
&lt;span class="c"&gt;# ARG GIT_COMMIT_ID&lt;/span&gt;

&lt;span class="c"&gt;### LAPTOP-BUILDS: memory-optimized settings for ARM64 (via --jobs cli-arg to cargo-build)&lt;/span&gt;
&lt;span class="c"&gt;### Modified RUSTFLAGS to include:&lt;/span&gt;
&lt;span class="c"&gt;###     -C opt-level=2 instead of the default -C opt-level=3 (less memory intensive)&lt;/span&gt;
&lt;span class="c"&gt;###     -C codegen-units=4 instead of the default -C codegen-units=1 (allows for better memory distribution)&lt;/span&gt;
&lt;span class="c"&gt;### For speed-optimized high-spec AWS-CodeBuild (arm64: 32 vCPUs, 64GB RAM)&lt;/span&gt;
&lt;span class="c"&gt;### RUSTFLAGS optimized for speed without target-cpu=native to avoid SIGILL:&lt;/span&gt;
&lt;span class="c"&gt;###     -C codegen-units=16 for optimal parallelization with 32 vCPUs&lt;/span&gt;
&lt;span class="c"&gt;###     -C target-feature=+v8a for ARM64 v8 optimizations&lt;/span&gt;
&lt;span class="c"&gt;###     -C opt-level=3 for maximum optimization (we have enough memory)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;:/opt/mold/bin"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nv"&gt;RUSTFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LINKER&lt;/span&gt;:+-C&lt;span class="p"&gt; link-arg=-fuse-ld=&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nv"&gt;$LINKER&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$RUSTFLAGS&lt;/span&gt;&lt;span class="s2"&gt; -C opt-level=3 -C codegen-units=16 -C target-feature=+v8a -C link-arg=-Wl,--threads=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    cargo build &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FEATURES&lt;/span&gt;:+--features&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;$FEATURES&lt;/span&gt; &lt;span class="nt"&gt;--features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stacktrace &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GPU&lt;/span&gt;:+--features&lt;span class="p"&gt;=gpu&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--bin&lt;/span&gt; qdrant &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;--jobs&lt;/span&gt; 32 &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;PROFILE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; dev &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;debug&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;else &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$PROFILE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv &lt;/span&gt;target/&lt;span class="nv"&gt;$PROFILE_DIR&lt;/span&gt;/qdrant /qdrant/qdrant
    &lt;span class="c"&gt;### Note: Since we are NOT cross-compiling, we do NOT need `/$(cargo --print-target-triple)` in the path above.&lt;/span&gt;

&lt;span class="c"&gt;# Generate SBOM&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;cargo &lt;span class="nb"&gt;install &lt;/span&gt;cargo-sbom &lt;span class="nt"&gt;--jobs&lt;/span&gt; 32 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    cargo sbom &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; qdrant.spdx.json

&lt;span class="c"&gt;# Dockerfile does not support conditional `FROM` directly.&lt;/span&gt;
&lt;span class="c"&gt;# To workaround this limitation, we use a multi-stage build with a different base images which have equal name to ARG value.&lt;/span&gt;

&lt;span class="c"&gt;# Base image for Qdrant.&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;public.ecr.aws/docker/library/debian:bookworm-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;qdrant-cpu&lt;/span&gt;

&lt;span class="c"&gt;# ### Only needed if NVIDIA GPU is required !!&lt;/span&gt;
&lt;span class="c"&gt;# # Base images for Qdrant with nvidia GPU support.&lt;/span&gt;
&lt;span class="c"&gt;# FROM nvidia/opengl:1.2-glvnd-devel-ubuntu22.04 AS qdrant-gpu-nvidia&lt;/span&gt;
&lt;span class="c"&gt;# # Set non-interactive mode for apt-get.&lt;/span&gt;
&lt;span class="c"&gt;# ENV DEBIAN_FRONTEND=noninteractive&lt;/span&gt;
&lt;span class="c"&gt;# # Set NVIDIA driver capabilities. By default, all capabilities are disabled.&lt;/span&gt;
&lt;span class="c"&gt;# ENV NVIDIA_DRIVER_CAPABILITIES compute,graphics,utility&lt;/span&gt;
&lt;span class="c"&gt;# # Copy Nvidia ICD loader file into the container.&lt;/span&gt;
&lt;span class="c"&gt;# COPY --from=builder /qdrant/lib/gpu/nvidia_icd.json /etc/vulkan/icd.d/&lt;/span&gt;
&lt;span class="c"&gt;# # Override maintainer label. Nvidia base image have it's own maintainer label.&lt;/span&gt;
&lt;span class="c"&gt;# LABEL maintainer="Qdrant Team &amp;lt;info@qdrant.tech&amp;gt;"&lt;/span&gt;


&lt;span class="c"&gt;# ### Only needed if AMD-GPU is required !!&lt;/span&gt;
&lt;span class="c"&gt;# FROM rocm/dev-ubuntu-22.04 AS qdrant-gpu-amd&lt;/span&gt;
&lt;span class="c"&gt;# # Set non-interactive mode for apt-get.&lt;/span&gt;
&lt;span class="c"&gt;# ENV DEBIAN_FRONTEND=noninteractive&lt;/span&gt;
&lt;span class="c"&gt;# # Override maintainer label. AMD base image have it's own maintainer label.&lt;/span&gt;
&lt;span class="c"&gt;# LABEL maintainer="Qdrant Team &amp;lt;info@qdrant.tech&amp;gt;"&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;qdrant-cpu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;qdrant&lt;/span&gt;
&lt;span class="c"&gt;# ### Only needed if NVidia or AMD-GPU is required !!&lt;/span&gt;
&lt;span class="c"&gt;# FROM qdrant-${GPU:+gpu-}${GPU:-cpu} AS qdrant&lt;/span&gt;

&lt;span class="c"&gt;# Install GPU dependencies&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GPU&lt;/span&gt;

&lt;span class="k"&gt;RUN if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GPU&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    libvulkan1 &lt;span class="se"&gt;\
&lt;/span&gt;    libvulkan-dev &lt;span class="se"&gt;\
&lt;/span&gt;    vulkan-tools &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Install additional packages into the container.&lt;/span&gt;
&lt;span class="c"&gt;# E.g., the debugger of choice: gdb/gdbserver/lldb.&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; PACKAGES&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; ca-certificates tzdata libunwind8 &lt;span class="nv"&gt;$PACKAGES&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get clean &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt; /var/cache/debconf/&lt;span class="k"&gt;*&lt;/span&gt; /var/lib/dpkg/status-old

&lt;span class="c"&gt;# Copy Qdrant source files into the container. Useful for debugging.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# To enable, set `SOURCES` to *any* non-empty string. E.g., 1/true/enable/whatever.&lt;/span&gt;
&lt;span class="c"&gt;# (Note, that *any* non-empty string would work, so 0/false/disable would enable the option as well.)&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; SOURCES&lt;/span&gt;

&lt;span class="c"&gt;# Dockerfile does not support conditional `COPY` instructions (e.g., it's impossible to do something&lt;/span&gt;
&lt;span class="c"&gt;# like `if [ -n "$SOURCES" ]; then COPY ...; fi`), so we *hack* conditional `COPY` by abusing&lt;/span&gt;
&lt;span class="c"&gt;# parameter expansion and `COPY` wildcards support. 😎&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; DIR=${SOURCES:+/qdrant/src}&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder ${DIR:-/null?} $DIR/&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; DIR=${SOURCES:+/qdrant/lib}&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder ${DIR:-/null?} $DIR/&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; DIR=${SOURCES:+/usr/local/cargo/registry/src}&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder ${DIR:-/null?} $DIR/&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; DIR=${SOURCES:+/usr/local/cargo/git/checkouts}&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder ${DIR:-/null?} $DIR/&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; DIR=""&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; APP=/qdrant&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; USER_ID=0&lt;/span&gt;

&lt;span class="k"&gt;RUN if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        groupadd &lt;span class="nt"&gt;--gid&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; qdrant&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        useradd &lt;span class="nt"&gt;--uid&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--gid&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; qdrant&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/storage &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/snapshots&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER_ID&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$USER_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=$USER_ID:$USER_ID /qdrant/qdrant "$APP"/qdrant&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=$USER_ID:$USER_ID /qdrant/qdrant.spdx.json "$APP"/qdrant.spdx.json&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=$USER_ID:$USER_ID /qdrant/config "$APP"/config&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=$USER_ID:$USER_ID /qdrant/tools/entrypoint.sh "$APP"/entrypoint.sh&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=$USER_ID:$USER_ID /static "$APP"/static&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; "$APP"&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; "$USER_ID:$USER_ID"&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; TZ=Etc/UTC \&lt;/span&gt;
    RUN_MODE=production

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 6333&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 6334&lt;/span&gt;

&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; org.opencontainers.image.title="Qdrant"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; org.opencontainers.image.description="Official Qdrant image"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; org.opencontainers.image.url="https://qdrant.com/"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; org.opencontainers.image.documentation="https://qdrant.com/docs"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; org.opencontainers.image.source="https://github.com/qdrant/qdrant"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; org.opencontainers.image.vendor="Qdrant"&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./entrypoint.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leaving the last &lt;code&gt;CMD&lt;/code&gt; intact, since we will be overriding &lt;code&gt;entrypoint&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  APPENDIX
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Get-Started &lt;a href="https://dev.to/seetamraju/vector-database-qdrant-cluster-on-ecs-fargate-2jak"&gt;article # 1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; A &lt;a href="https://dev.to/seetamraju/design-vector-database-qdrant-cluster-38ab"&gt;article # 2&lt;/a&gt; has FULL DETAILs on the Critical-Design &amp;amp; Key-requirements that influenced/constrained/forced the final implementation.&lt;/li&gt;
&lt;li&gt; A &lt;a href="https://dev.to/seetamraju/snapshots-data-restore-vector-database-qdrant-cluster-33gj"&gt;article # 3&lt;/a&gt; re: Snapshots.&lt;/li&gt;
&lt;li&gt; A separate GitLab-repo contains the full CDK-Construct.&lt;/li&gt;
&lt;li&gt; Assumption: You'll OK to CUSTOM-build the Qdrant Container-IMAGE (using a custom Dockerfile) using Qdrant's github.  &lt;a href="https://dev.to/seetamraju/vector-database-qdrant-cluster-dockerfile-4lal"&gt;article # 4&lt;/a&gt; for a sensible/defensible &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;/ End&lt;/p&gt;

</description>
      <category>database</category>
      <category>rust</category>
      <category>docker</category>
      <category>aws</category>
    </item>
    <item>
      <title>Snapshots &amp; Data-RESTORE: Vector-Database: Qdrant-Cluster</title>
      <dc:creator>Sarma</dc:creator>
      <pubDate>Wed, 31 Dec 2025 03:31:37 +0000</pubDate>
      <link>https://dev.to/seetamraju/snapshots-data-restore-vector-database-qdrant-cluster-33gj</link>
      <guid>https://dev.to/seetamraju/snapshots-data-restore-vector-database-qdrant-cluster-33gj</guid>
      <description>&lt;p&gt;This belongs to an article set.  Go to "Get-Started" &lt;a href="https://dev.to/seetamraju/vector-database-qdrant-cluster-on-ecs-fargate-2jak"&gt;article # 1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To state the obvious, it VERY IMPORTANT to very-frequently TEST the validity of SnapShots - manually.  All Vector-databases are Not as robust as good ol' SQL-Databases, to fully trust the backups/snapshots.&lt;/p&gt;

&lt;p&gt;The following manual steps walk you thru' (first) how to take/make snapshots and then to "restore/recover" from a snapshot.&lt;/p&gt;

&lt;h2&gt;
  
  
  (A) HOW-TO - creating snapshots
&lt;/h2&gt;

&lt;h3&gt;
  
  
  OPTION 1: in an AUTOMATED manner
&lt;/h3&gt;

&lt;p&gt;We need a new Lambda.  See sample code at bottom.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Manually
&lt;/h3&gt;

&lt;p&gt;If you want to MANUALLY trigger a snapshot of an entire-collection, just simply use the Qdrant's built-in RestApi:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;POST /collections/${collection_name}/snapshots&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Do this either via your choice of RestApi-client, or via Qdrant's Dashboard (via ALB like &lt;em&gt;&lt;a href="httpS://my-custom-alb-domain.mycompany.com/dashboard" rel="noopener noreferrer"&gt;httpS://my-custom-alb-domain.mycompany.com/dashboard&lt;/a&gt;&lt;/em&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  (B) HOW-TO:- viewing snapshots
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Use the Qdrant-Dashboard:

&lt;ol&gt;
&lt;li&gt; Important: Use the API-Key/Token for security!!&lt;/li&gt;
&lt;li&gt; Important: Use &lt;code&gt;httpS&lt;/code&gt; !&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt; Invoke &lt;code&gt;POST collections/${collection_name}/points/scroll&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;  Save post-response to a temporary local file, to help you track what you will do per this article.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt; Confirm (using the post-response) that you have a NEW snapshot, that we shall refer to as &lt;code&gt;snapshot # 1&lt;/code&gt; (a.k.a. &lt;code&gt;baseline&lt;/code&gt; ) of the collection.

&lt;ul&gt;
&lt;li&gt; FYI: You can easily recognize snapshots based on the TIMESTAMP that is part of the snapshot's name.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt; If relying on automated-snapshots, wait for the &lt;strong&gt;next&lt;/strong&gt; new snapshot to be created.&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  (C) HOW-TO:- prep for VERIFYING the snapshots
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Make a change to your vector database.

&lt;ul&gt;
&lt;li&gt;  If you are using Qdrant's &lt;code&gt;test_collection&lt;/code&gt;, then insert a new Point: &lt;code&gt;PointStruct(id=6, vector=[0.30, 0.05, 0.10, 0.40], payload={"city": "Bengaluru"})&lt;/code&gt; (Don't ask me about the numbers)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Trigger a new snapshot (manually or via scheduled-automation).&lt;/li&gt;
&lt;li&gt; &lt;code&gt;POST collections/${collection_name}/points/scroll&lt;/code&gt; (repeat)

&lt;ul&gt;
&lt;li&gt;  --COMPARE-- this response with the contents of a temporary local file.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Confirm you have a NEW snapshot (called &lt;code&gt;snapshot # 2&lt;/code&gt;) of the collection (AFTER the above update).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  (D) HOW-TO:- Restore/Recover
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Snapshots stored locally inside Qdrant-cluster.
&lt;/h3&gt;

&lt;p&gt;FYI: Qdrant stores ALL snapshots that you create/trigger, under &lt;code&gt;/qdrant/snapshots&lt;/code&gt;, but YOU should rely on Qdrant's API (&lt;code&gt;../scroll&lt;/code&gt;) to list/access/download/upload snapshots.&lt;/p&gt;

&lt;p&gt;Invoke the following &lt;code&gt;POST&lt;/code&gt; api (with json-body) to do a restore using &lt;code&gt;baseline&lt;/code&gt; a.k.a. &lt;code&gt;snapshot # 1&lt;/code&gt; :-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;PUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/collections/$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;collection_name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;/snapshots/recover`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"file:///qdrant/storage/snapshots/${collection_name}/${collection_name}-YYYY-MM-DD-HH-MM-SS.snapshot"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; Do a query to verify that data change (that you did in sub-section &lt;code&gt;(C)&lt;/code&gt;) .. is MISSING !&lt;/li&gt;
&lt;li&gt; Restore from an LATEST snapshot # 2.&lt;/li&gt;
&lt;li&gt; Do a query to verify that data change (that you did in sub-section &lt;code&gt;(C)&lt;/code&gt;) .. is BACK !&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  From s3-bucket
&lt;/h3&gt;

&lt;p&gt;Warning: The Fargate-Task's(Container) role will need S3-access. Out of scope of this article.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Restore from an old snapshot # 1/baseline using this example:-
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;PUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/collections/$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;collection_name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;/snapshots/recover`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://??????????.s3.us-east-2.amazonaws.com/${collection_name}-1046358132872257-2025-12-10-20-31-22.snapshot"&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; Do a query to verify that data change (that you did in sub-section &lt;code&gt;(C)&lt;/code&gt;) .. is MISSING !&lt;/li&gt;
&lt;li&gt; Restore from an LATEST snapshot # 2.&lt;/li&gt;
&lt;li&gt; Do a query to verify that data change (that you did in sub-section &lt;code&gt;(C)&lt;/code&gt;) .. is BACK !&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  (E) HOW-TO:- Lambda to create AUTOMATED-Snapshots
&lt;/h2&gt;

&lt;p&gt;This Lambda will trigger snapshots periodically, and will also &lt;strong&gt;COPY&lt;/strong&gt; those snapshots to an S3-bucket.  FYI: I instead prefer mounting an EFS-FileSystem onto &lt;code&gt;/qdrant/snapshots/&lt;/code&gt; of the Container (runnning as a Fargate-Task), and having EFS replicated to a 2nd region.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;traceback&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_lambda_powertools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Tracer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Metrics&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_lambda_powertools.logging&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;correlation_paths&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_lambda_powertools.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MetricUnit&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Tracer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;metrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Metrics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;s3_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;secrets_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;secretsmanager&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;### -------------------------------------------
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_api_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secrets_manager_arn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Retrieve API key from AWS Secrets Manager&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;secrets_manager_arn&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;secrets_manager_arn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Secrets Manager ARN not provided&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Retrieving API key from Secrets Manager ARN: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;secrets_manager_arn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&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;secrets_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_secret_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SecretId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;secrets_manager_arn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&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;SecretString&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API key not found in secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API key retrieved successfully ✅&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;

&lt;span class="c1"&gt;### -------------------------------------------
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_qdrant_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qdrant_fqdn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Construct Qdrant URL using Service Discovery FQDN&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;qdrant_fqdn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QDRANT_FQDN environment variable not set&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;qdrant_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;qdrant_fqdn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:6333&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Qdrant URL: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;qdrant_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;qdrant_url&lt;/span&gt;

&lt;span class="c1"&gt;### -------------------------------------------
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;determine_snapshot_frequency&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Determine which snapshot frequency to use based on current time
    Returns: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;15min&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hourly&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;daily&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, or &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;monthly&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Monthly: 1st of month at 8 AM UTC
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minute&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;monthly&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="c1"&gt;# Daily: Every day at 8 AM UTC
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minute&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;daily&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="c1"&gt;# Hourly: Top of every hour
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minute&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hourly&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="c1"&gt;# Default: 15-minute intervals
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;15min&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;### -------------------------------------------
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_collections&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qdrant_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get list of all collections&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;collections_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;qdrant_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/collections&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;headers&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;api-key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Getting collections from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;collections_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&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;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collections_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&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="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to get collections: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;collections_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&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;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;collections_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;collections&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])]&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found collections: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;

&lt;span class="c1"&gt;### -------------------------------------------
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_collection_snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qdrant_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Create a snapshot for a specific collection
    Returns snapshot metadata
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;snapshot_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;qdrant_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/collections/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/snapshots&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;headers&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;api-key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Creating snapshot for collection &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;snapshot_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&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;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshot_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&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="n"&gt;status_code&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to create snapshot for collection &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;snapshot_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Snapshot created for collection &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&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;snapshot_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;snapshot_data&lt;/span&gt;

&lt;span class="c1"&gt;### -------------------------------------------
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;download_collection_snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qdrant_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snapshot_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Download collection snapshot from Qdrant&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;download_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;qdrant_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/collections/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/snapshots/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;snapshot_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;headers&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;api-key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Downloading snapshot from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;download_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&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;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;download_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to download snapshot: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;

&lt;span class="c1"&gt;### -------------------------------------------
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_to_s3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s3_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Upload snapshot to S3&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Uploading to s3://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s3_key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;s3_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s3_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Upload complete ✅&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;### -------------------- main -----------------------
&lt;/span&gt;&lt;span class="nd"&gt;@logger.inject_lambda_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;correlation_id_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;correlation_paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API_GATEWAY_REST&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@tracer.capture_lambda_handler&lt;/span&gt;
&lt;span class="nd"&gt;@metrics.log_metrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capture_cold_start_metric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Lambda function to create and manage Qdrant snapshots
    Implements RPO of 15 minutes with S3 lifecycle-based retention
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Get environment variables
&lt;/span&gt;        &lt;span class="n"&gt;secrets_manager_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AK_ARN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;secrets_manager_arn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AK_ARN environment-variable NOT set&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;qdrant_fqdn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;QDRANT_FQDN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;qdrant_fqdn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;QDRANT_FQDN environment-variable NOT set&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;tier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;TIER&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;tier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TIER environment-variable NOT set&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;cpu_arch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CPU_ARCH&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;cpu_arch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CPU_ARCH environment-variable NOT set&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# ecs_cluster_name = os.getenv('ECS_CLUSTER_NAME')
&lt;/span&gt;        &lt;span class="c1"&gt;# fargate_container_name = os.getenv('FARGATE_CONTAINER_NAME')
&lt;/span&gt;
        &lt;span class="c1"&gt;# Validate required environment variables
&lt;/span&gt;        &lt;span class="c1"&gt;# if not all([secrets_manager_arn, qdrant_fqdn, tier, cpu_arch, ecs_cluster_name, fargate_container_name]):
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;secrets_manager_arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qdrant_fqdn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cpu_arch&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
            &lt;span class="n"&gt;missing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AK_ARN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;secrets_manager_arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;QDRANT_FQDN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;qdrant_fqdn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;TIER&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CPU_ARCH&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cpu_arch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="c1"&gt;# 'ECS_CLUSTER_NAME': ecs_cluster_name,
&lt;/span&gt;                &lt;span class="c1"&gt;# 'FARGATE_CONTAINER_NAME': fargate_container_name
&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Missing required environment variables: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Get API key and Qdrant URL
&lt;/span&gt;        &lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_api_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secrets_manager_arn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;qdrant_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_qdrant_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qdrant_fqdn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Determine snapshot frequency
&lt;/span&gt;        &lt;span class="n"&gt;frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;determine_snapshot_frequency&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Snapshot frequency: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Get collections
&lt;/span&gt;        &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_collections&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qdrant_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No collections found - skipping snapshot creation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Content-Type&lt;/span&gt;&lt;span class="sh"&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;application/json&lt;/span&gt;&lt;span class="sh"&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;body&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&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;No collections found - no snapshots created&lt;/span&gt;&lt;span class="sh"&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;frequency&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;frequency&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;# Get S3 bucket name
&lt;/span&gt;        &lt;span class="n"&gt;bucket_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SNAPSHOT_S3_BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SNAPSHOT_S3_BUCKET_NAME environment-variable NOT set&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Create snapshots for each collection
&lt;/span&gt;        &lt;span class="n"&gt;snapshots_created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;total_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;collection_name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# Create collection snapshot
&lt;/span&gt;                &lt;span class="n"&gt;snapshot_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_collection_snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qdrant_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;snapshot_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;snapshot_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;snapshot_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Snapshot name not found for collection &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;continue&lt;/span&gt;

                &lt;span class="c1"&gt;# Download snapshot
&lt;/span&gt;                &lt;span class="n"&gt;snapshot_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;download_collection_snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qdrant_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snapshot_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Downloaded snapshot for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshot_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bytes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="c1"&gt;# Upload to S3
&lt;/span&gt;                &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%Y%m%d-%H%M%S&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;s3_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tier&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cpu_arch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;snapshot_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="c1"&gt;# s3_key = f"{tier}-{cpu_arch}/{frequency}/{collection_name}/{timestamp}-{snapshot_name}"
&lt;/span&gt;                &lt;span class="nf"&gt;upload_to_s3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s3_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snapshot_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="n"&gt;snapshots_created&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;collection&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;snapshot_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;snapshot_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3_location&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s3_key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;size_bytes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshot_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="n"&gt;total_size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshot_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to create snapshot for collection &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="c1"&gt;# Emit metrics
&lt;/span&gt;        &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_metric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SnapshotsCreated&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MetricUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshots_created&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_metric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TotalSnapshotSize&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MetricUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;total_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Content-Type&lt;/span&gt;&lt;span class="sh"&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;application/json&lt;/span&gt;&lt;span class="sh"&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;body&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Collection snapshots created successfully&lt;/span&gt;&lt;span class="sh"&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;frequency&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;snapshots_created&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshots_created&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;collections&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;snapshots&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;snapshots_created&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;note&lt;/span&gt;&lt;span class="sh"&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;Retention managed by S3 lifecycle policies&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;traceback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print_exc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error creating snapshot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_metric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SnapshotError&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MetricUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;Content-Type&lt;/span&gt;&lt;span class="sh"&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;application/json&lt;/span&gt;&lt;span class="sh"&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;body&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Error creating snapshot: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&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;h2&gt;
  
  
  APPENDIX
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Get-Started &lt;a href="https://dev.to/seetamraju/vector-database-qdrant-cluster-on-ecs-fargate-2jak"&gt;article # 1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; A &lt;a href="https://dev.to/seetamraju/design-vector-database-qdrant-cluster-38ab"&gt;article # 2&lt;/a&gt; has FULL DETAILs on the Critical-Design &amp;amp; Key-requirements that influenced/constrained/forced the final implementation.&lt;/li&gt;
&lt;li&gt; A &lt;a href="https://dev.to/seetamraju/snapshots-data-restore-vector-database-qdrant-cluster-33gj"&gt;article # 3&lt;/a&gt; re: Snapshots.&lt;/li&gt;
&lt;li&gt; A separate GitLab-repo contains the full CDK-Construct.&lt;/li&gt;
&lt;li&gt; Assumption: You'll OK to CUSTOM-build the Qdrant Container-IMAGE (using a custom Dockerfile) using Qdrant's github.  &lt;a href="https://dev.to/seetamraju/vector-database-qdrant-cluster-dockerfile-4lal"&gt;article # 4&lt;/a&gt; for a sensible/defensible &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;/ End&lt;/p&gt;

</description>
      <category>database</category>
      <category>tutorial</category>
      <category>devops</category>
      <category>aws</category>
    </item>
    <item>
      <title>Design: Vector-Database: Qdrant-Cluster on Fargate</title>
      <dc:creator>Sarma</dc:creator>
      <pubDate>Wed, 31 Dec 2025 03:00:59 +0000</pubDate>
      <link>https://dev.to/seetamraju/design-vector-database-qdrant-cluster-38ab</link>
      <guid>https://dev.to/seetamraju/design-vector-database-qdrant-cluster-38ab</guid>
      <description>&lt;p&gt;This is 2nd part of a set of articles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Get-Started &lt;a href="https://dev.to/seetamraju/vector-database-qdrant-cluster-on-ecs-fargate-2jak"&gt;article # 1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;This&lt;/strong&gt; &lt;a href="https://dev.to/seetamraju/design-vector-database-qdrant-cluster-38ab"&gt;article # 2&lt;/a&gt; has FULL DETAILs on the Critical-Design &amp;amp; Key-requirements that influenced/constrained/forced the final implementation.&lt;/li&gt;
&lt;li&gt; A &lt;a href="https://dev.to/seetamraju/snapshots-data-restore-vector-database-qdrant-cluster-33gj"&gt;article # 3&lt;/a&gt; re: Snapshots.&lt;/li&gt;
&lt;li&gt; A separate GitLab-repo contains the full CDK-Construct.&lt;/li&gt;
&lt;li&gt; Assumption: You'll OK to CUSTOM-build the Qdrant Container-IMAGE (using a custom Dockerfile) using Qdrant's github.  &lt;a href="https://dev.to/seetamraju/vector-database-qdrant-cluster-dockerfile-4lal"&gt;article # 4&lt;/a&gt; for a sensible/defensible &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;All of this (CDK &amp;amp; Clusters) is too much to handle?  Seek professional-services from Qdrant.tech; If not, me/my current-employer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary of Challenges:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; This is a &lt;strong&gt;Database&lt;/strong&gt;!&lt;/li&gt;
&lt;li&gt; Need &lt;strong&gt;multi-AZ resiliency&lt;/strong&gt; when cluster is created.&lt;/li&gt;
&lt;li&gt; Must automatically &lt;strong&gt;maintain multi-AZ&lt;/strong&gt; resiliency even after innumerable Node-Failures; How to delegate to AWS (ECS), to maintain this multi-AZ balancing by automatically REPLACING nodes, ensuring resiliency &amp;amp; DLP (data-loss protection).&lt;/li&gt;
&lt;li&gt; Need high-speed robust multi-AZ replication (of storage/data)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Avoid re-inventing&lt;/strong&gt; the wheel, that too .. on AWS Cloud&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Avoid deploying any of my own&lt;/strong&gt; mechanisms for:-

&lt;ol&gt;
&lt;li&gt; Resiliency, incl. automatically spinning up a new "node", if any node becomes unhealthy.&lt;/li&gt;
&lt;li&gt; inter-node / inter-process Communication&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Replication of Storage-layer&lt;/strong&gt; or just database-data&lt;/li&gt;
&lt;li&gt; Monitoring &amp;amp; Alerting&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt; Storage-Persistence challenges

&lt;ol&gt;
&lt;li&gt; Qdrant, like any database, does &lt;strong&gt;file-Locking&lt;/strong&gt; (highly parallel threads attempting to lock the file-system)&lt;/li&gt;
&lt;li&gt; Multiple copies of data across AZs in sync all the time, almost instantly ready to copy to a new replica-node on any AZ-outage.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;EBS Volumes&lt;/strong&gt; can NOT be replicated to another AZ or to another Region.&lt;/li&gt;
&lt;li&gt; A "crash" (of a &lt;strong&gt;single-node Qdrant-Vector-DB&lt;/strong&gt;) can leave the contents of the EBS Volumes in a "failed-data-integrity" status that may prevent the replacement node from spinning up;  This is a &lt;strong&gt;classic "&lt;em&gt;Crashed-Database problem&lt;/em&gt;"&lt;/strong&gt; which has NO simple solution.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt; Self-managed cluster:

&lt;ol&gt;
&lt;li&gt; Using Fargate-Tasks/Containers as "nodes" (instead of traditional EC2s)&lt;/li&gt;
&lt;li&gt; Qdrant requires a cluster's "nodes/tasks/containers" .. .. to be spun up in certain SEQUENCE; in a specific order.

&lt;ul&gt;
&lt;li&gt;  Worse, Qdrant MANDATES that each SUBSEQUENT "node/task/container" to be given information about ANY one of the "preceding" ones.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; If the &lt;strong&gt;1st Qdrant&lt;/strong&gt; "node/task/container" that was created goes down, replacing it requires special-handling!  That is, replacement of "startup/1st" node/task/container uses a different &lt;code&gt;Entrypoint&lt;/code&gt; command (compared to the "origina" &lt;code&gt;Entrypoint&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Fargate-Tasks/Containers, by design, have &lt;strong&gt;ephemeral PRIVATE-IPs&lt;/strong&gt;.  That's good!&lt;/li&gt;
&lt;li&gt; Database comes with a "&lt;strong&gt;Qdrant-Dashboard&lt;/strong&gt;" (website); Its built-in into the database! And, we need access to it, but do NOT want the database to use public-IPs!&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Snapshots-based restore/recovery&lt;/strong&gt; (usually a manual activity) requires use of this "Qdrant-Dashboard".&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Challenge&lt;/strong&gt;: Fargate tasks have ephemeral private IPs, no predictable FQDNs/server-names.  Connecting all "nodes/tasks/containers" together as a cluster

&lt;ul&gt;
&lt;li&gt; &lt;strong&gt;Best Solution&lt;/strong&gt; is &lt;strong&gt;ECS Service Discovery&lt;/strong&gt; :- It creates predictable DNS names (e.g., &lt;code&gt;qdrant-cluster.my-ecs-cluster-name.local&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt; Alternatives are: &lt;strong&gt;Parameter Store Coordination&lt;/strong&gt; ,  &lt;strong&gt;EFS-Based Coordination&lt;/strong&gt; (Leverage existing EFS) , &lt;strong&gt;ALB + Health Check Discovery&lt;/strong&gt; (Use load balancer for discovery)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Blame "Raft" protocol and/or Qdrant, but .. .. you are REQUIRED to fully bring up 1st node/task/container, and then wait for it to finish initialization, before bringing up any other node/task/container.

&lt;ul&gt;
&lt;li&gt;  Can it be designed simpler !? Sure! You are welcome anytime to put in a PR on Qdrant's official-GitHub.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; No more EFS (instead of EBS) as primary filesystem, for ALL Task-instances/Containers.  Reason: Qdrant will detect it and stop working.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  Must-have Requirements
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Per Qdrant's documentation, with their reliance on &lt;strong&gt;"Raft" protocol&lt;/strong&gt;, they recommend a cluster of minimum 3 "nodes/tasks/containers".  Studying Qdrant's documentation clarifies that, it should be &lt;strong&gt;3 per shard&lt;/strong&gt;, and that for &lt;strong&gt;future-proofing&lt;/strong&gt; they recommend starting with &lt;em&gt;2 shards per collection&lt;/em&gt;.

&lt;ul&gt;
&lt;li&gt;  So, in total 6! That is, 2 nodes/containers/tasks in each AZ, each containing a different Shard.  Thereby, each Shard is replicated across 3 AZs.&lt;/li&gt;
&lt;li&gt;  You MAY be able to make do with 4, but RAFT's behavior needs studying.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Backup&lt;/strong&gt; Snapshots

&lt;ol&gt;
&lt;li&gt; Must have &lt;strong&gt;100% integrity&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Ideally, &lt;strong&gt;avoid taking down&lt;/strong&gt; the "database/cluster/writers" before taking a snapshot&lt;/li&gt;
&lt;li&gt; Leverage the database's &lt;strong&gt;INHERENT-ability&lt;/strong&gt; to create snapshots.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Avoid storage-level&lt;/strong&gt; snapshots (given the decades-long history of the "&lt;em&gt;Crashed-Database problem&lt;/em&gt;")&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Maintanence Scenarios&lt;/strong&gt;:-

&lt;ol&gt;
&lt;li&gt; Automation of snapshots as appropriate for &lt;strong&gt;D.R. R.P.O&lt;/strong&gt;;  We chose an &lt;strong&gt;RPO of 15&lt;/strong&gt; minutes.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;RETENTION&lt;/strong&gt;: Snapshots made every 15 minutes; Last 24 hourly-snapshots to be retained; Last 30 daily-snapshots to be retained; Last 12 monthly-snapshots to be retained.&lt;/li&gt;
&lt;li&gt; Based on your IT-process sophistication and based on the size of your collection(s), &lt;strong&gt;choose between&lt;/strong&gt; Shard-level snapshots -versus- collection-level snapshots.  We chose collection-snapshots.  Note: &lt;strong&gt;Avoid storage-level&lt;/strong&gt; snapshots.&lt;/li&gt;
&lt;li&gt; Resilient-storage of snapshots &lt;strong&gt;across regions&lt;/strong&gt;.  FYI: Typical choices are S3, EFS &amp;amp; AWS-Backup.  "Standard" EBS can Not do this.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Security:

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;httpS&lt;/code&gt;&lt;/strong&gt; from outside Vpc, via a &lt;strong&gt;single-endpoint no matter what&lt;/strong&gt; changes happen within Qdrant-cluster.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;"native" ApiKey&lt;/strong&gt;, to protect the "Qdrant Dashboard" &amp;amp; Qdrant-apis.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Monitoring:

&lt;ol&gt;
&lt;li&gt; Basic "node" monitoring (O/S level)&lt;/li&gt;
&lt;li&gt; Database monitoring (Qdrant-API-level)&lt;/li&gt;
&lt;li&gt; Shard-health monitoring (Qdrant-API-level)&lt;/li&gt;
&lt;li&gt; Replacement-of-nodes  monitoring&lt;/li&gt;
&lt;li&gt; Periodic "101% Healthy" monitoring-checks (actually trying to create a new "test-collection", insert data &amp;amp; run a query).&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Software Licensing (for use within a Proprietary product sold to Clients)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Will Qdrant's licensing support this Enterprise-grade Cluster being used in a commercial-product?  Answer is yes, as of Dec 2025.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Advanced:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; if I have a large-cluster, then I should be utilizing &lt;strong&gt;every single&lt;/strong&gt; Qdrant-node (for faster "sharded" queries).&lt;/li&gt;
&lt;li&gt; Not sure how Qdrant leverages the 3-copies (across 3 AZs) of each shard for improving query-performance.&lt;/li&gt;
&lt;li&gt; But, should you ever want to use a VPC-Lambda to split-up queries across ALL Qdrant-nodes, the Lambda must be able to get the list of PRIVATE-IPs of each Qdrant-node.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  Decisions
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Use &lt;strong&gt;pure&lt;/strong&gt; CDK / &lt;strong&gt;pure&lt;/strong&gt; CloudFormation.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Avoid&lt;/strong&gt; &lt;strong&gt;Custom-Resources&lt;/strong&gt; in CDK/CloudFormation at all costs.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Avoid&lt;/strong&gt; having to "do stuff" &lt;strong&gt;after&lt;/strong&gt; deployment of aws-resoures.
&lt;strong&gt;Avoid&lt;/strong&gt; using Lambdas completely w.r.t. ANY Cluster modifications (post-deployment).

&lt;ol&gt;
&lt;li&gt; The cluster will need to &lt;strong&gt;run 24x7x365 and must self-adjust&lt;/strong&gt;.  Having lambdas run periodically is Never viable for resilience.&lt;/li&gt;
&lt;li&gt; Periodically doing &lt;strong&gt;Scale-up/down&lt;/strong&gt; will significantly raise the risk of Data-Loss. No more Scaling-down on a schedule, daily.

&lt;ol&gt;
&lt;li&gt; Mechanism to take a snapshot (of --EACH-- shard or of --EACH-- collection).  This is important due to implications of SHARDING.&lt;/li&gt;
&lt;li&gt; Ability to save SnapShots onto an EFS-filesystem (either on-demand or scheduled)&lt;/li&gt;
&lt;li&gt; Restore a collection from a SnapShot instead (avoid DIY).&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;/li&gt;

&lt;li&gt; These articles require you to &lt;strong&gt;custom-build your own Docker-container-image&lt;/strong&gt; (from Qdrant's source-code in GitHub).

&lt;ul&gt;
&lt;li&gt; If your personal-requirements/constraints are forcing you to avoid changing/enhancing the &lt;code&gt;Dockerfile&lt;/code&gt; (so that any "drop-in" official Qdrant image will also work) .. you'll then need 3 different ECS-services.  Details are out of scope of these articles.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt; Only &lt;code&gt;production&lt;/code&gt; will use a Qdrant-6-node-Cluster.  &lt;strong&gt;All other environments&lt;/strong&gt; will use a single-node Qdrant-container/Task (which has a scheduled daily downtime).&lt;/li&gt;

&lt;li&gt; Should clusters be considered too expensive, then a single-node Qdrant Vector-DB along with Snapshots can potentially support an RPO of 15-minutes, but this will require automation to thoroughly test the "snapshots".&lt;/li&gt;

&lt;li&gt; Each Fargate-Task will have just one Container; In these articles, they are synonymous.&lt;/li&gt;

&lt;li&gt; Native non-storage SnapShots is mandatory from day 1, needed no matter what the final architecture &amp;amp; design.&lt;/li&gt;

&lt;li&gt; Snapshots (with zero-overhead) should be automatically stored resiliently across regions.  Choices are S3, EFS &amp;amp; AWS Backup.  Simplest is EFS (with multi-region replication), by mounting to &lt;code&gt;/qdrant/snapshots&lt;/code&gt; into the container, so we chose EFS &lt;strong&gt;JUST for&lt;/strong&gt; storing snapshots &lt;strong&gt;only&lt;/strong&gt;.&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;End.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Stop using ANY Container-image</title>
      <dc:creator>Sarma</dc:creator>
      <pubDate>Sat, 04 Oct 2025 18:48:03 +0000</pubDate>
      <link>https://dev.to/seetamraju/stop-using-any-container-image-24kp</link>
      <guid>https://dev.to/seetamraju/stop-using-any-container-image-24kp</guid>
      <description>&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Stop using:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;docker run --privileged --rm tonistiigi/binfmt --install all ;&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;❌ Warning !! This is an unverified 3rd-party container-image&lt;br&gt;
Speaking in his favor, Tonis Tiigi puts in a lot of effort 👍 to keep this Container-image updated.&lt;br&gt;
FYI: Life and software is a marathon that no single person can keep doing all alone for long.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;docker run --rm --privileged public.ecr.aws/k1t8c0v2/multiarch/qemu-user-static:latest --reset -p yes&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;❌ Warning !! Un-verified 3rd-party container-image.  It is NOT being supported❌ and became outdated❌ a few years ago.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;docker run --rm --privileged multiarch/qemu-user-static --reset -p yes&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This .. &lt;del&gt;is&lt;/del&gt; was .. (in contrast to tonistiigi) community-supported.  As is so common, it is NOT being supported❌ and became outdated❌ a few years ago.&lt;/p&gt;
&lt;h2&gt;
  
  
  Instead (for 2026) use ✅
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    &lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; qemu-system-arm qemu-user-static binfmt-support &lt;span class="p"&gt;;&lt;/span&gt;

    docker run &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; public.ecr.aws/eks-distro-build-tooling/binfmt-misc:qemu-v7.0.0 &lt;span class="nt"&gt;--install&lt;/span&gt; all &lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;How can you simply test/verify, if this will work for you?&lt;br&gt;
See Appendix below.&lt;/p&gt;

&lt;p&gt;Switch from &lt;code&gt;apt&lt;/code&gt; to &lt;code&gt;dnf&lt;/code&gt; or other package-manager as appropriate for your needs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why bother?
&lt;/h2&gt;

&lt;p&gt;(Want an alternative to -NOT- do the above, see next section.)&lt;/p&gt;

&lt;p&gt;This is a Security topic.  Either you get it, and eagerly (repeat: eagerly) implement the recommendations.&lt;br&gt;
Or, you are part of the problem.&lt;/p&gt;

&lt;p&gt;Here's another way to look at this article:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You: &lt;em&gt;But.. but..  Mx. Police officer! Everyone else is going 25xph over the speed limit&lt;/em&gt;.&lt;br&gt;
Police Officer: &lt;em&gt;Fact is -YOU- are going 25xph over the speed limit.  Here's your speeding ticket/चालान.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you'd like to STILL argue/defend yourself about how the speeding-ticket says you being discriminated against ..&lt;br&gt;
.. stop reading this.&lt;/p&gt;

&lt;p&gt;This is a Security topic.  Either you get it, and eagerly (repeat: eagerly) implement the recommendations.&lt;br&gt;
Or, you are part of the problem.&lt;/p&gt;

&lt;p&gt;How many times have I seen people point this out:- That His Excellency, the current US President, frequently says "&lt;em&gt;I've heard SO MANY other people say.. ..&lt;/em&gt;" on something most people have Not heard about.&lt;br&gt;
We -ALL- do the SAME EXACT thing (like demonstrated in this article below) sooo frequently .. but, Mr. President, &lt;em&gt;we are holier than thou&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is a Security topic.  Either you get it, and eagerly (repeat: eagerly) implement the recommendations.&lt;br&gt;
Or, you are part of the problem.&lt;/p&gt;

&lt;p&gt;"One guy" (one of us) writes -ONE- blog about using a container owned by &lt;code&gt;tonistiigi&lt;/code&gt;.&lt;br&gt;
Another "One guy" (one of us) writes -ONE- post about using a container owned by &lt;code&gt;k1t8c0v2&lt;/code&gt;.&lt;br&gt;
For years on end, ..&lt;br&gt;
(repeating it) For years on end, ..&lt;br&gt;
developers keep repeating "&lt;em&gt;I've heard -SO MANY- people use .. .. ..&lt;/em&gt;"&lt;br&gt;
Exactly like in an echo chamber, these 3rd-party container-images get broadcast all over, and magically they becomes trustworthy!&lt;/p&gt;

&lt;p&gt;In this day and age, where the lineage of SBOM is so important, why do you use 3rd party container-images?&lt;/p&gt;

&lt;p&gt;What is "SBOM", you ask?!&lt;br&gt;
You are part of the problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Alternative
&lt;/h2&gt;

&lt;p&gt;There is an alternative to NOT doing anything from this article.&lt;br&gt;
This alternative was so well demonstrated during a recent media-covered DATA-LEAK incident about a "Tea App".&lt;br&gt;
On face-value, it appears that the Product-Manager(s), senior-most management and/or developers did Nothing to prevent the leak.&lt;br&gt;
No Security whatsoever.&lt;br&gt;
Everything about women expressing private opinions, including their IDs, were exposed.  The women were humiliated.&lt;/p&gt;

&lt;p&gt;So, do you know what the company owning "Tea App" did?&lt;br&gt;
They took advantage of the narrative, that the "&lt;em&gt;Men who were detailed in the Tea App, conspired with the so many other Men of this world, and hacked the app, to take some kind of revenge on those women&lt;/em&gt;".&lt;br&gt;
"&lt;em&gt;The product-manager(s), senior-management and/or developers were GOOD people with the proverbial golden ring HALO above their heads&lt;/em&gt;".&lt;/p&gt;

&lt;p&gt;"&lt;em&gt;The road to hell is paved with good intentions&lt;/em&gt;" is a meaningless jumble of words.&lt;/p&gt;

&lt;p&gt;Bottomline: Instead of "fixing potential security" problems, get a Communication Major/Degree and "indulge in Media Spin" like the above couple of paras, instead.&lt;br&gt;
That is the only other alternative (to this article) that I am aware of.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why should I trust the above recommendation?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; AWS Owns and Maintains this specific container image.&lt;/li&gt;
&lt;li&gt; AWS uses it in the context of EKS (at least for now).&lt;/li&gt;
&lt;li&gt; Added benefit: Within AWS, you will Not hit limits on your "docker pull/run.." !

&lt;ul&gt;
&lt;li&gt;  FYI: I use it extensively in my Pipelines, which pull this image so so many times.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Appendix - How to SIMPLY confirm that the replacement works?
&lt;/h2&gt;

&lt;p&gt;Within an O/S on X86 ..&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="nv"&gt;OtherChipArch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"linux/arm64"&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;update-binfmts &lt;span class="nt"&gt;--display&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;arm &lt;span class="p"&gt;;&lt;/span&gt;
    docker buildx &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Sanity-check the ARM64-chipset execution via X86-Docker-in-Docker..."&lt;/span&gt;
    docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OtherChipArch&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;  public.ecr.aws/sam/build-python3.12:latest python &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You know how to do the other way around.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix -- For those who love mega-deep-dives
&lt;/h2&gt;

&lt;p&gt;The ABOVE-docker-cmd installs the QEMU binaries + registers them with binfmt_misc, enabling QEMU to execute non-native file formats for emulation&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;## These `update-binfmt` cmds are --NOT-- sufficient.  We need the "PRIVILEGED" docker-cmd !!!&lt;/span&gt;
    &lt;span class="c"&gt;## Register QEMU interpreters for multi-platform builds&lt;/span&gt;
    &lt;span class="c"&gt;## CLI-manpage: https://manpages.debian.org/testing/binfmt-support/update-binfmts.8.en.html&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;update-binfmts &lt;span class="nt"&gt;--enable&lt;/span&gt; qemu-arm &lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;update-binfmts &lt;span class="nt"&gt;--enable&lt;/span&gt; qemu-armeb &lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;update-binfmts &lt;span class="nt"&gt;--display&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;arm &lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As learn more fine nuances, I'll add it here.&lt;/p&gt;

&lt;p&gt;/ EoF&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Running AWS Glue locally on a Windows Laptop</title>
      <dc:creator>Sarma</dc:creator>
      <pubDate>Tue, 16 Jan 2024 03:58:39 +0000</pubDate>
      <link>https://dev.to/seetamraju/running-aws-glue-locally-on-a-windows-laptop-5d4l</link>
      <guid>https://dev.to/seetamraju/running-aws-glue-locally-on-a-windows-laptop-5d4l</guid>
      <description>&lt;p&gt;by Udaybhaskar Sarma Seetamraju&lt;br&gt;
&lt;a href="mailto:ToSarma@gmail.com"&gt;ToSarma@gmail.com&lt;/a&gt;&lt;br&gt;
Dec 31 2023&lt;/p&gt;
&lt;h1&gt;
  
  
  Highest-level Context
&lt;/h1&gt;

&lt;p&gt;If you are into “&lt;strong&gt;Shift-Left&lt;/strong&gt;” (whether re: Testing, Security, or Replicating-problems-on-developer-laptop, etc ..), then this article is for you.&lt;/p&gt;

&lt;p&gt;Objective: Towards enabling up to 5x developer-productivity by allowing developers to robustly SIMULATE the Cloud-environment on a Windows-Laptop.&lt;/p&gt;
&lt;h1&gt;
  
  
  Quick Summary
&lt;/h1&gt;

&lt;p&gt;Aiming for very simple single command, based on &lt;code&gt;python&lt;/code&gt;-scripts --&amp;gt; to execute your python-code as a Glue Job’s build &lt;strong&gt;locally on your Microsoft-Windows Laptop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Even if your company disallows AWS CLI credentials, I have a section on how to significantly raise your productivity, in debugging/developing your python-code.&lt;/p&gt;

&lt;p&gt;To state the obvious, everything in here is 100% Python-Scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You should aim to have your software work on &lt;code&gt;arm64&lt;/code&gt; containers, which invariably is cheapest compute on cloud.   More below.&lt;/p&gt;
&lt;h1&gt;
  
  
  Problem Statements
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Using Git for capturing ALL code-changes while simultaneously copying-n-pasting into AWS Glue-Studio (for testing/troubleshooting) is painful, error prone and frustrating.&lt;/li&gt;
&lt;li&gt;As time progresses, your Glue Job will become complicated and require more than one Python-script.Worse, you have one or more folder-hierarchies, all of which contain &lt;code&gt;PY&lt;/code&gt; files that you need to &lt;code&gt;import&lt;/code&gt;!&lt;/li&gt;
&lt;li&gt;Many developers prefer to develop/test/troubleshoot their code as “&lt;strong&gt;plain python&lt;/strong&gt;”, and Not as a Glue-Job.There is No good reason to deny such developers from doing just that.. .. while ensuring that code will work without any issues inside AWS Glue running locally on Windows-Laptop, and eventually work without issues inside Glue on AWS.

&lt;ol&gt;
&lt;li&gt;When running as “plain python”, there should be No runtime dependencies (like “&lt;code&gt;import awsglue&lt;/code&gt;”).&lt;/li&gt;
&lt;li&gt;When running as “plain python”, there should be No &lt;code&gt;spark&lt;/code&gt;-dependency.&lt;/li&gt;
&lt;li&gt;When running as “plain python”, all inputs/files should be on local laptop’s filesystem.  All output should be written to local filesystem only.&lt;/li&gt;
&lt;li&gt;When running as “plain python”, all information from Glue-Catalog should be available &lt;strong&gt;OFF&lt;/strong&gt;line (as a Python &lt;code&gt;Dict&lt;/code&gt; object)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;How to proactively ensure the Glue Job will work on all chip-architectures - without having to scramble later? How to explicitly utilize all &lt;code&gt;x86_64/amd64/arm64&lt;/code&gt; architectures locally on your Windows-Laptop?&lt;/li&gt;
&lt;li&gt;If the Enterprise does Not allow Laptops to have AWS-Credentials (in &lt;code&gt;~/.aws/credentials&lt;/code&gt; file);Even so, how can I &lt;strong&gt;EFFICIENTLY&lt;/strong&gt; test/debug the python-code file locally on my laptop, even as it needs access to Glue Catalog and/or S3 buckets?&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;
  
  
  Get started!
&lt;/h1&gt;

&lt;p&gt;Based on your needs on AWS choose between these 2 lines below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;BUILDPLATFORM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"linux/amd64"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;BUILDPLATFORM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"linux/arm64"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run these commands in a &lt;code&gt;Powershell&lt;/code&gt; window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;DOCKER_DEFAULT_PLATFORM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;${BUILDPLATFORM}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;TARGETPLATFORM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;${DOCKER_DEFAULT_PLATFORM}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$WORK_AREA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;~&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$WORK_AREA&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://gitlab.com/tosarma/macbook-m1.git&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  To try out a sample ..
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;macbook-m1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AWS-Glue/src&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;pip3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$WORK_AREA&lt;/span&gt;&lt;span class="nx"&gt;/macbook-m1/AWS-Glue/bin/run-glue-job-LOCALLY.py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;sample-glue-job.py&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;: Without the benefit of “&lt;code&gt;docker&lt;/code&gt; cli”, you get &lt;strong&gt;ZERO&lt;/strong&gt; visibility into the progress of docker-activity. This is due to use of un-friendly Docker’s Python APIs, because of which the python-code _ WILL _  _ HANG _ for a long time!&lt;/p&gt;

&lt;p&gt;To repeat, “&lt;code&gt;run-glue-job-LOCALLY.py&lt;/code&gt;” will hang with NO output, for roughly 2-to-5 minutes (depending on how much CPU and MEMORY you have allocated to the Docker-Desktop, as well as speed of your internet connection).&lt;/p&gt;

&lt;p&gt;Important: I only tested using &lt;code&gt;Python3.11&lt;/code&gt;; No other Python version tested.&lt;/p&gt;

&lt;h1&gt;
  
  
  Ready to use it for your own Glue-Script?
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;First, read the full details within the &lt;a href="https://gitlab.com/tosarma/macbook-m1/-/blob/main/AWS-Glue/README.md?ref_type=heads"&gt;&lt;code&gt;macbook-m1/AWS-Glue/README.md&lt;/code&gt;&lt;/a&gt; file.&lt;/li&gt;
&lt;li&gt; Copy the &lt;code&gt;*.py&lt;/code&gt; files in the &lt;code&gt;macbook-m1/AWS-Glue/src/common&lt;/code&gt; subfolder into --&amp;gt; YOUR project’s TOPMOST-folder.

&lt;ul&gt;
&lt;li&gt; ATTENTION: the files &lt;code&gt;./src/common/*.py&lt;/code&gt; must exist in your project, after you are done copying.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Make sure to edit your &lt;code&gt;PY&lt;/code&gt; file, to look like the example file provided (&lt;a href="https://gitlab.com/tosarma/macbook-m1/-/blob/main/AWS-Glue/src/sample-glue-job.py?ref_type=heads"&gt;&lt;code&gt;sample-glue-job.py&lt;/code&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt; From your project root, run:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;your-project-ROOT-folder&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$WORK_AREA&lt;/span&gt;&lt;span class="nx"&gt;/macbook-m1/AWS-Glue/bin/run-glue-job-LOCALLY.py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;path/to/your/file.py&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are having problems importing the new files under &lt;code&gt;./src/common&lt;/code&gt;, then try adding this command below and then retry the above command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;PYTHONPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-project's-root-folder"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is your Python code-base consisting of multiple files across multiple folder-hierarchies?&lt;br&gt;
See section below titled “&lt;em&gt;Complex folder-hierarchies?&lt;/em&gt;”&lt;/p&gt;
&lt;h1&gt;
  
  
  Want to change the CLI-arguments?
&lt;/h1&gt;

&lt;p&gt;Three simple steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Edit the file  &lt;code&gt;macbook-m1/AWS-Glue/src/common/cli_utils.py&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Look inside “&lt;code&gt;process_all_argparse_cli_args()&lt;/code&gt;” and make changes in that function.&lt;/li&gt;
&lt;li&gt;Look inside “&lt;code&gt;process_std_glue_cli_args()&lt;/code&gt;” and make changes inside that function.

&lt;ul&gt;
&lt;li&gt;  Note: make sure to make similar changes in above steps 2 &amp;amp; 3.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;You would like to support a new cli-arg as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--JOB_NAME   123_ABC&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Insert a new line at (say) &lt;strong&gt;line # 125&lt;/strong&gt; for &lt;code&gt;JOB_NAME&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  This will ensure your code will get the value &lt;code&gt;123_ABC&lt;/code&gt; when running __ INSIDE __ AWS-Glue !!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Insert a new line at (say) &lt;strong&gt;line # 78&lt;/strong&gt; for &lt;code&gt;--JOB_NAME&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; This will allow you to run your python-code as a PLAIN python-command and read this CLI-arg.&lt;/li&gt;
&lt;li&gt; See more re: this in a following section titled “&lt;em&gt;running as a PLAIN python-command&lt;/em&gt;”&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Never edit the file:&lt;br&gt;
&lt;code&gt;macbook-m1/AWS-Glue/src/common/glue_utils.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The files “&lt;code&gt;common.py&lt;/code&gt;” and “&lt;code&gt;names.py&lt;/code&gt;” in that same folder can be edited.   Feel free to play around with them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tips, Issues &amp;amp; Errors
&lt;/h2&gt;

&lt;p&gt;See &lt;strong&gt;Appendix&lt;/strong&gt; sections, for tips on configuring Docker-DESKTOP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt;: Want to automatically cleanup/delete  the Docker-containers - after they exit?&lt;br&gt;
&lt;strong&gt;Answer&lt;/strong&gt;: INSERT the cli-arg “&lt;code&gt;--cleanup&lt;/code&gt;” BEFORE the python-script-name, to that “&lt;code&gt;run-glue-job-LOCALLY.py&lt;/code&gt;” script.&lt;/p&gt;
&lt;h1&gt;
  
  
  Advanced User - Complex folder-hierarchies?
&lt;/h1&gt;

&lt;p&gt;Is your Python code-base consisting of &lt;strong&gt;multiple files across&lt;/strong&gt; multiple folder-hierarchies?&lt;br&gt;
&lt;strong&gt;Are you aware&lt;/strong&gt; that Glue requires you to &lt;strong&gt;ZIP up all&lt;/strong&gt; those OTHER python-files into a single Zip-file?&lt;br&gt;
FYI only - this requirement is driven by Spark!&lt;/p&gt;

&lt;p&gt;That script “&lt;code&gt;run-glue-job-LOCALLY.py&lt;/code&gt;” will &lt;strong&gt;automatically&lt;/strong&gt; do that for you --&amp;gt; that is, it will &lt;strong&gt;automatically look UNDER&lt;/strong&gt; the current-working-directory, and find all &lt;code&gt;**/*.py&lt;/code&gt; files and put them in a &lt;strong&gt;temporary&lt;/strong&gt; ZIP-file.&lt;br&gt;
The script will then automatically pass it on to &lt;em&gt;Glue-inside-Docker&lt;/em&gt; (running on your laptop).&lt;/p&gt;

&lt;p&gt;If you need to import &lt;code&gt;PY&lt;/code&gt; files in parent/ancestor levels, you are in an UN-fortunate NO-Solution sitution.  FYI: Unlike on MacOS/Linux, you are _ NOT _ able to take advantage of MacOS/Linux “&lt;em&gt;symlink&lt;/em&gt;” (“&lt;code&gt;ln -s&lt;/code&gt;”) to those parent/ancestor files, where &lt;code&gt;git&lt;/code&gt; CLI can preserve these “&lt;em&gt;symlinks&lt;/em&gt;” as exactly just that.&lt;/p&gt;
&lt;h1&gt;
  
  
  No AWS Credentials on your laptop?
&lt;/h1&gt;

&lt;p&gt;For security-reasons, many companies are denying developers the AWS-credentials for AWS-CLI use.&lt;/p&gt;

&lt;p&gt;That means you have a showstopper -&amp;gt; re: locally testing/debugging your python-code, for scenarios like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;COPY INPUT-files from S3-buckets --&amp;gt; into the “current-working directory”.&lt;/li&gt;
&lt;li&gt;COPY OUTPUT-files from the “current-working directory” --&amp;gt; into S3-buckets.&lt;/li&gt;
&lt;li&gt;Lookup Glue Catalog.&lt;/li&gt;
&lt;li&gt;.. etc ..&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To workaround this restriction ..&lt;br&gt;
You need to write code that detects whether its running on a Windows-Laptop -&lt;em&gt;&lt;u&gt;versus&lt;/u&gt;&lt;/em&gt;- actually running inside AWS-Cloud.&lt;br&gt;
In other words, you need to “&lt;em&gt;Short-Circuit&lt;/em&gt;” all that code that interacts with AWS-APIs (Glue-Catalog, S3, ..) and mock the expected response from those AWS-APIs.&lt;/p&gt;

&lt;p&gt;If you use my script “&lt;code&gt;run-glue-job-LOCALLY.py&lt;/code&gt;”, it &lt;strong&gt;automatically&lt;/strong&gt; sets an &lt;em&gt;environment-variable called&lt;/em&gt; “&lt;code&gt;running_on_LAPTOP&lt;/code&gt;” when running your python-code inside a Docker-Glue container &lt;strong&gt;on your laptop&lt;/strong&gt;!!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How-To&lt;/strong&gt; “&lt;em&gt;short-circuit&lt;/em&gt;”:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if ( os.environ.get('running_on_LAPTOP') ):
    print( "!!!!!!!!!!!!!!!!! running on laptop !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" )
    .. ### assume S3-get is already done and file is available in current-directory
    .. ### assume Glue-Catalog-Query is already done and ..
       ###      the "JSON-Response" is available in current-directory as a JSON-file
    ..
else:
    ..
    ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To state the obvious, on AWS-Cloud, AWS-GLUE does __ NOT __ support environment-variables.&lt;br&gt;
So, this environment-variable called “&lt;code&gt;running_on_LAPTOP&lt;/code&gt;” will be &lt;strong&gt;UN&lt;/strong&gt;-defined when running inside AWS-Cloud.&lt;/p&gt;
&lt;h1&gt;
  
  
  Running as a plain python command
&lt;/h1&gt;

&lt;p&gt;EXAMPLE:&lt;br&gt;
I’m going to use the same “&lt;code&gt;macbook-m1/AWS-Glue/src/sample-glue-job.py&lt;/code&gt;” file, &lt;strong&gt;to show how to&lt;/strong&gt; run as PLAIN Python-program.&lt;/p&gt;

&lt;p&gt;FYI: My python-code in that &lt;code&gt;sample-glue-job.py&lt;/code&gt; expects the following 6 CLI-arguments (with their values).&lt;/p&gt;

&lt;p&gt;If you do _ NOT _ like this list of CLI-args, see section above titled “&lt;em&gt;Want to change the CLI-args?&lt;/em&gt;”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 sample-glue-job.py --ENV sandbox ^
     --commonDatabaseName MyCOMMONDATABASENAME ^
     --glueCatalogEntryName MyDICT ^
     --rawBucket MyRAWBUCKET ^
     --processedBucket MyPROCESSEDBUCKET  ^
     --finalBucket MyFINALBUCKET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want the ability to run both as a plain python-script as well as run it inside AWS-glue, you __ MUST __ replicate the structure and code within this “&lt;code&gt;macbook-m1/AWS-Glue/src/sample-glue-job.py&lt;/code&gt;”.&lt;/p&gt;

&lt;h1&gt;
  
  
  APPENDIX
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Running out of Disk-space or Memory?
&lt;/h2&gt;

&lt;p&gt;Screenshot below shows the recommended “high” settings.&lt;br&gt;
After building images, you can reduce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“CPU” can be lowered to “2”.&lt;/li&gt;
&lt;li&gt;“Memory” can be lowered to “4GB”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tDQZnJMm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wvy0t07w4vfsxvbgst4c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tDQZnJMm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wvy0t07w4vfsxvbgst4c.png" alt="Image description" width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;End of Article.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Running AWS Glue locally on a MacBook-M1</title>
      <dc:creator>Sarma</dc:creator>
      <pubDate>Mon, 08 Jan 2024 06:40:19 +0000</pubDate>
      <link>https://dev.to/seetamraju/running-aws-glue-locally-on-a-macbook-m1-3635</link>
      <guid>https://dev.to/seetamraju/running-aws-glue-locally-on-a-macbook-m1-3635</guid>
      <description>&lt;p&gt;by Udaybhaskar Sarma Seetamraju&lt;br&gt;
&lt;a href="mailto:ToSarma@gmail.com"&gt;ToSarma@gmail.com&lt;/a&gt;&lt;br&gt;
Dec 31 2023&lt;/p&gt;
&lt;h1&gt;
  
  
  Highest-level Context
&lt;/h1&gt;

&lt;p&gt;If you are into “&lt;strong&gt;Shift-Left&lt;/strong&gt;” (whether re: Testing, Security, or Replicating-problems-on-developer-laptop, etc ..), then this article is for you.&lt;/p&gt;

&lt;p&gt;For the very first time that you switch to an M1-chipset based MacBooks (from intel-chip based MacBooks) .. Productivity is significantly impacted when doing development/testing/troubleshooting “locally” on your laptop.  Out-of-scope of this article is supporting those switching from Windoze.&lt;/p&gt;

&lt;p&gt;Towards enabling up to 5x developer-productivity by allowing developers to robustly SIMULATE the Cloud-environment on a laptop — I have the following series of articles re: M1-chipset based MacBooks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;a href="https://dev.to/seetamraju/running-aws-codebuild-locally-on-a-macbook-m1-9eo"&gt;Running AWS CodeBuild locally&lt;/a&gt; on MacBook-M1.

&lt;ul&gt;
&lt;li&gt; Running Containers based on older &lt;code&gt;Ubuntu 20.04&lt;/code&gt; (released in the year 2020) as well as on the newer &lt;code&gt;Ubuntu 22.04&lt;/code&gt; (released in the year 2022)&lt;/li&gt;
&lt;li&gt; Running Containers based on &lt;code&gt;arm64&lt;/code&gt;-based Linux&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; (This) Running &lt;code&gt;AWS Glue&lt;/code&gt; locally on MacBook-M1.
Various scenarios covered like: you do Not have “aws credentials” on your Laptop (forcing you to mock all the AWS API calls like &lt;code&gt;S3 GET&lt;/code&gt;, Glue-Catalog queries, etc..)&lt;/li&gt;
&lt;li&gt; &lt;a href="https://dev.to/seetamraju/creating-arm64aarch64-docker-images-on-a-macbook-m1-new-security-related-best-practices-4jn9"&gt;New Security-related Best-Practices&lt;/a&gt; when creating &lt;code&gt;arm64/aarch64&lt;/code&gt; Docker-Images on a MacBook-M1.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;
  
  
  Quick Summary
&lt;/h1&gt;

&lt;p&gt;Aiming for very simple single command, based on &lt;code&gt;bash&lt;/code&gt;-shell scripts --&amp;gt; to execute your python-code as a Glue Job’s build &lt;strong&gt;locally on your MacBook-M1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In addition, I have a section on how to significantly raise your productivity, in debugging/developing your python-code, even if your company denies your AWS CLI credentials.&lt;/p&gt;

&lt;p&gt;To state the obvious, everything here is 100% Python + Bash-Scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You should aim to have your software work on &lt;code&gt;arm64&lt;/code&gt; containers, which invariably is cheapest compute on cloud.   More below.&lt;/p&gt;
&lt;h1&gt;
  
  
  Problem Statements
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Using Git for capturing ALL code-changes while simultaneously copying-n-pasting into AWS Glue-Studio (for testing/troubleshooting) is painful, error prone and frustrating.&lt;/li&gt;
&lt;li&gt;As time progresses, your Glue Job will become complicated and require more than one Python-script. Worse, you have one or more folder-hierarchies, all of which contain &lt;code&gt;PY&lt;/code&gt; files that you need to &lt;code&gt;import&lt;/code&gt;!&lt;/li&gt;
&lt;li&gt;Many developers prefer to develop/test/troubleshoot their code as “&lt;strong&gt;plain python&lt;/strong&gt;”, and Not as a Glue-Job. There is No good reason to deny such developers from doing just that.. .. while ensuring that code will work without any issues inside AWS Glue running locally on MacBook-M1, and eventually work without issues inside Glue on AWS.

&lt;ol&gt;
&lt;li&gt;When running as “plain python”, there should be No runtime dependencies (like “&lt;code&gt;import awsglue&lt;/code&gt;”).&lt;/li&gt;
&lt;li&gt;When running as “plain python”, there should be No &lt;code&gt;spark&lt;/code&gt;-dependency.&lt;/li&gt;
&lt;li&gt;When running as “plain python”, all inputs/files should be on local laptop’s filesystem.  All output should be written to local filesystem only.&lt;/li&gt;
&lt;li&gt;When running as “plain python”, all information from Glue-Catalog should be available &lt;strong&gt;OFF&lt;/strong&gt;line (as a Python &lt;code&gt;Dict&lt;/code&gt; object)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;How to proactively ensure the Glue Job will work on all chip-architectures - without having to scramble later? How to explicitly utilize all &lt;code&gt;x86_64/amd64/arm64/aarch64&lt;/code&gt; architectures locally on laptop?&lt;/li&gt;
&lt;li&gt;If the Enterprise does Not allow Laptops to have AWS-Credentials (in &lt;code&gt;~/.aws/credentials&lt;/code&gt; file); Even so, how can I &lt;strong&gt;EFFICIENTLY&lt;/strong&gt; test/debug the python-code file locally on my laptop, even as it needs access to Glue Catalog and/or S3 buckets?&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;
  
  
  Get started!
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;export BUILDPLATFORM="linux/aarch64"&lt;/code&gt;&lt;br&gt;
ONLY when for running on MacBook-M1 laptop, if you'd like to take advantage of native-performance boost !!&lt;/p&gt;

&lt;p&gt;Based on your needs on AWS choose between these 2:&lt;br&gt;
&lt;code&gt;export BUILDPLATFORM="linux/&lt;/code&gt;&lt;del&gt;&lt;code&gt;amd64&lt;/code&gt;&lt;/del&gt;&lt;code&gt;"&lt;/code&gt;&lt;br&gt;
&lt;code&gt;export BUILDPLATFORM="linux/arm64"&lt;/code&gt;/&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export DOCKER_DEFAULT_PLATFORM="${BUILDPLATFORM}"
export TARGETPLATFORM="${DOCKER_DEFAULT_PLATFORM}"

WORK_AREA=~
cd ${WORK_AREA}
git clone https://gitlab.com/tosarma/macbook-m1.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  To try out a sample ..
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd macbook-m1
cd AWS-Glue/src
${WORK_AREA}/macbook-m1/AWS-Glue/bin/run-glue-job-LOCALLY.sh  sample-glue-job.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  No Bash?  Want Python instead?
&lt;/h2&gt;

&lt;p&gt;Just replace the “&lt;code&gt;.sh&lt;/code&gt;” with “&lt;code&gt;.py&lt;/code&gt;” — in the script name &lt;code&gt;“run-glue-job-LOCALLY&lt;/code&gt;” (as shown above).&lt;br&gt;
And, of course, you must insert “&lt;code&gt;python3&lt;/code&gt;” at the very beginning of the CLI (this is a platform-independent advice).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;: Without the benefit of “&lt;code&gt;docker&lt;/code&gt; cli”, you get &lt;strong&gt;ZERO&lt;/strong&gt; visibility into the progress of docker-activity. This is due to use of un-friendly Docker’s Python APIs, because of which the python-code _ WILL _  _ HANG _ for a long time!&lt;/p&gt;

&lt;p&gt;To repeat, “&lt;code&gt;run-glue-job-LOCALLY.py&lt;/code&gt;” will hang with NO output, for roughly 2-to-5 minutes (depending on how much CPU and MEMORY you have allocated to the Docker-Desktop, as well as speed of your internet connection).&lt;/p&gt;

&lt;p&gt;Important - Note these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; I only tested using &lt;code&gt;Python3.11&lt;/code&gt;; No other Python version tested.&lt;/li&gt;
&lt;li&gt; PRE-REQUISITES:

&lt;ul&gt;
&lt;li&gt; &lt;code&gt;pip3 install docker&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;
  
  
  Ready to use it for your own Glue-Script?
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;First, read the full details within the &lt;a href="https://gitlab.com/tosarma/macbook-m1/-/blob/main/AWS-Glue/README.md?ref_type=heads"&gt;&lt;code&gt;macbook-m1/AWS-Glue/README.md&lt;/code&gt;&lt;/a&gt; file.&lt;/li&gt;
&lt;li&gt; Copy the &lt;code&gt;*.py&lt;/code&gt; files in the &lt;code&gt;macbook-m1/AWS-Glue/src/common&lt;/code&gt; subfolder into --&amp;gt; YOUR project’s TOPMOST-folder.

&lt;ul&gt;
&lt;li&gt; ATTENTION: the files &lt;code&gt;./src/common/*.py&lt;/code&gt; must exist in your project, after you are done copying.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Make sure to edit your &lt;code&gt;PY&lt;/code&gt; file, to look like the example file provided (&lt;a href="https://gitlab.com/tosarma/macbook-m1/-/blob/main/AWS-Glue/src/sample-glue-job.py?ref_type=heads"&gt;&lt;code&gt;sample-glue-job.py&lt;/code&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt; From your project root, run:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd &amp;lt;your-project's-root-folder&amp;gt;

${WORK_AREA}/macbook-m1/AWS-Glue/bin/run-glue-job-LOCALLY.sh \
          path/to/your/file.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you are having problems importing the new files under &lt;code&gt;./src/common&lt;/code&gt;, then try adding this command below and then retry the above command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export PYTHONPATH="your-project's-root-folder"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is your Python code-base consisting of multiple files across multiple folder-hierarchies?&lt;br&gt;
See section below titled “&lt;em&gt;Complex folder-hierarchies?&lt;/em&gt;”&lt;/p&gt;
&lt;h1&gt;
  
  
  Want to change the CLI-arguments?
&lt;/h1&gt;

&lt;p&gt;Three simple steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Edit the file  &lt;code&gt;macbook-m1/AWS-Glue/src/common/cli_utils.py&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Look inside “&lt;code&gt;process_all_argparse_cli_args()&lt;/code&gt;” and make changes in that function.&lt;/li&gt;
&lt;li&gt;Look inside “&lt;code&gt;process_std_glue_cli_args()&lt;/code&gt;” and make changes inside that function.

&lt;ul&gt;
&lt;li&gt;  Note: make sure to make similar changes in above steps 2 &amp;amp; 3.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You would like to support a new cli-arg as:&lt;br&gt;
*  &lt;code&gt;--JOB_NAME   123_ABC&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Insert a new line at (say) &lt;strong&gt;line # 125&lt;/strong&gt; for &lt;code&gt;JOB_NAME&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  This will ensure your code will get the value &lt;code&gt;123_ABC&lt;/code&gt; when running __ INSIDE __ AWS-Glue !!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Insert a new line at (say) &lt;strong&gt;line # 78&lt;/strong&gt; for &lt;code&gt;--JOB_NAME&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; This will allow you to run your python-code as a PLAIN python-command and read this CLI-arg.&lt;/li&gt;
&lt;li&gt; See more re: this in a following section titled “&lt;em&gt;running as a PLAIN python-command&lt;/em&gt;”&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Never edit the file:&lt;br&gt;
&lt;code&gt;macbook-m1/AWS-Glue/src/common/glue_utils.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The files “&lt;code&gt;common.py&lt;/code&gt;” and “&lt;code&gt;names.py&lt;/code&gt;” in that same folder can be edited.   Feel free to play around with them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tips, Issues &amp;amp; Errors
&lt;/h2&gt;

&lt;p&gt;See &lt;strong&gt;Appendix&lt;/strong&gt; sections, for tips on configuring Docker-DESKTOP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt;: Want to automatically cleanup/delete  the Docker-containers - after they exit?&lt;br&gt;
&lt;strong&gt;Answer&lt;/strong&gt;: INSERT the cli-arg “&lt;code&gt;--cleanup&lt;/code&gt;” BEFORE the python-filename, to that “&lt;code&gt;run-glue-job-LOCALLY.sh&lt;/code&gt;” script.&lt;/p&gt;
&lt;h1&gt;
  
  
  Advanced User - Complex folder-hierarchies?
&lt;/h1&gt;

&lt;p&gt;Is your Python code-base consisting of &lt;strong&gt;multiple files across&lt;/strong&gt; multiple folder-hierarchies?&lt;br&gt;
&lt;strong&gt;Are you aware&lt;/strong&gt; that Glue requires you to &lt;strong&gt;ZIP up all&lt;/strong&gt; those OTHER python-files into a single Zip-file?&lt;br&gt;
FYI only - this requirement is driven by Spark!&lt;/p&gt;

&lt;p&gt;That script “&lt;code&gt;run-glue-job-LOCALLY.sh&lt;/code&gt;” will &lt;strong&gt;automatically&lt;/strong&gt; do that for you --&amp;gt; that is, it will &lt;strong&gt;automatically look UNDER&lt;/strong&gt; the current-working-directory, and find all &lt;code&gt;**/*.py&lt;/code&gt; files and put them in a &lt;strong&gt;temporary&lt;/strong&gt; ZIP-file.&lt;br&gt;
The script will then automatically pass it on to &lt;em&gt;Glue-inside-Docker&lt;/em&gt; (running on your laptop).&lt;/p&gt;

&lt;p&gt;If you need to import &lt;code&gt;PY&lt;/code&gt; files in parent/ancestor levels, I recommend that you add a “&lt;em&gt;symlink&lt;/em&gt;” (Linux command “&lt;code&gt;ln -s&lt;/code&gt;”) to those files, and put that &lt;em&gt;symlink&lt;/em&gt; in your current-working-folder.&lt;br&gt;
Git will &lt;strong&gt;preserve&lt;/strong&gt; these “&lt;em&gt;symlinks&lt;/em&gt;” as exactly just that.  It will NOT convert them into files.  So, feel better already!&lt;/p&gt;
&lt;h1&gt;
  
  
  No AWS Credentials on your laptop?
&lt;/h1&gt;

&lt;p&gt;For security-reasons, many companies are denying developers the AWS-credentials for AWS-CLI use.&lt;/p&gt;

&lt;p&gt;That means you have a showstopper -&amp;gt; re: locally testing/debugging your python-code, for scenarios like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;COPY INPUT-files from S3-buckets --&amp;gt; into the “current-working directory”.&lt;/li&gt;
&lt;li&gt;COPY OUTPUT-files from the “current-working directory” --&amp;gt; into S3-buckets.&lt;/li&gt;
&lt;li&gt;Lookup Glue Catalog.&lt;/li&gt;
&lt;li&gt;.. etc ..&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To workaround this restriction .. &lt;br&gt;
You need to write code that detects whether its running on a MacBook-M1 -&lt;em&gt;&lt;u&gt;versus&lt;/u&gt;&lt;/em&gt;- actually running inside AWS-Cloud.&lt;br&gt;
In other words, you need to “&lt;em&gt;Short-Circuit&lt;/em&gt;” all that code that interacts with AWS-APIs (Glue-Catalog, S3, ..) and mock the expected response from those AWS-APIs.&lt;/p&gt;

&lt;p&gt;If you use my script “&lt;code&gt;run-glue-job-LOCALLY.sh&lt;/code&gt;”, it &lt;strong&gt;automatically&lt;/strong&gt; sets an &lt;em&gt;environment-variable called&lt;/em&gt; “&lt;code&gt;running_on_LAPTOP&lt;/code&gt;” when running your python-code inside a Docker-Glue container &lt;strong&gt;on your laptop&lt;/strong&gt;!!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How-To&lt;/strong&gt; “&lt;em&gt;short-circuit&lt;/em&gt;”:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if ( os.environ.get('running_on_LAPTOP') ):
    print( "!!!!!!!!!!!!!!!!! running on laptop !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" )
    .. ### assume S3-get is already done and file is available in current-directory
    .. ### assume Glue-Catalog-Query is already done and ..
       ###      the "JSON-Response" is available in current-directory as a JSON-file
    ..
else:
    ..
    ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To state the obvious, on AWS-Cloud, AWS-GLUE does __ NOT __ support environment-variables.&lt;br&gt;
So, this environment-variable called “&lt;code&gt;running_on_LAPTOP&lt;/code&gt;” will be &lt;strong&gt;UN&lt;/strong&gt;-defined when running inside AWS-Cloud.&lt;/p&gt;
&lt;h1&gt;
  
  
  Running as a plain python command
&lt;/h1&gt;

&lt;p&gt;EXAMPLE:&lt;br&gt;
I’m going to use the same “&lt;code&gt;macbook-m1/AWS-Glue/src/sample-glue-job.py&lt;/code&gt;” file, &lt;strong&gt;to show how to&lt;/strong&gt; run as PLAIN Python-program.&lt;/p&gt;

&lt;p&gt;FYI: My python-code in that &lt;code&gt;sample-glue-job.py&lt;/code&gt; expects the following 6 CLI-arguments (with their values).&lt;/p&gt;

&lt;p&gt;If you do _ NOT _ like this list of CLI-args, see section above titled “&lt;em&gt;Want to change the CLI-args?&lt;/em&gt;”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 sample-glue-job.py --ENV sandbox \
     --commonDatabaseName MyCOMMONDATABASENAME \
     --glueCatalogEntryName MyDICT \
     --rawBucket MyRAWBUCKET \
     --processedBucket MyPROCESSEDBUCKET  \
     --finalBucket MyFINALBUCKET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want the ability to run both as a plain python-script as well as run it inside AWS-glue, you __ MUST __ replicate the structure and code within this “&lt;code&gt;macbook-m1/AWS-Glue/src/sample-glue-job.py&lt;/code&gt;”.&lt;/p&gt;

&lt;h1&gt;
  
  
  APPENDIX
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Docker-Desktop settings for &lt;code&gt;aarch64&lt;/code&gt;-chipset
&lt;/h2&gt;

&lt;p&gt;See screenshot below.&lt;br&gt;
Turn &lt;strong&gt;ON&lt;/strong&gt; the setting titled “&lt;em&gt;Use containerd for pulling and storing images&lt;/em&gt;”!&lt;br&gt;
Note: for other scenarios, you may have to turn it &lt;strong&gt;OFF&lt;/strong&gt;.&lt;br&gt;
I can’t help explain this crazy conflicting instructions.&lt;br&gt;
As of 2023, this is a Docker-on-MacBook issue, resolvable only by Docker + Apple Corp.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gusAPDp8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uut29wgwqjnc94cv1idg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gusAPDp8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uut29wgwqjnc94cv1idg.png" alt="Image description" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running out of Disk-space or Memory?
&lt;/h2&gt;

&lt;p&gt;Screenshot below shows the recommended “high” settings.&lt;br&gt;
After building images, you can reduce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“CPU” can be lowered to “2”.&lt;/li&gt;
&lt;li&gt;“Memory” can be lowered to “4GB”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FYI only - To run on a MacBook-M1, many &lt;code&gt;amd64&lt;/code&gt; emulated containers like &lt;code&gt;Neo4j v4.x&lt;/code&gt; will frequently fail, unless you provide Docker with a minimum of 5 cpus and 8GB of RAM!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tDQZnJMm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wvy0t07w4vfsxvbgst4c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tDQZnJMm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wvy0t07w4vfsxvbgst4c.png" alt="Image description" width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;End of Article.&lt;/p&gt;

</description>
      <category>macbook</category>
      <category>aws</category>
      <category>glue</category>
      <category>docker</category>
    </item>
    <item>
      <title>Running AWS CodeBuild locally on a MacBook-M1</title>
      <dc:creator>Sarma</dc:creator>
      <pubDate>Mon, 08 Jan 2024 04:35:58 +0000</pubDate>
      <link>https://dev.to/seetamraju/running-aws-codebuild-locally-on-a-macbook-m1-9eo</link>
      <guid>https://dev.to/seetamraju/running-aws-codebuild-locally-on-a-macbook-m1-9eo</guid>
      <description>&lt;p&gt;by Udaybhaskar Sarma Seetamraju&lt;br&gt;
&lt;a href="mailto:ToSarma@gmail.com"&gt;ToSarma@gmail.com&lt;/a&gt;&lt;br&gt;
Dec 31 2023&lt;/p&gt;
&lt;h1&gt;
  
  
  Highest-level Context
&lt;/h1&gt;

&lt;p&gt;If you are into “&lt;strong&gt;Shift-Left&lt;/strong&gt;” (whether re: Testing, Security, or Replicating-problems-on-developer-laptop, etc ..), then this article is for you.&lt;/p&gt;

&lt;p&gt;For the very first time that you switch to an M1-chipset based MacBooks (from intel-chip based MacBooks) .. Productivity is significantly impacted when doing development/testing/troubleshooting “locally” on your laptop.  Out-of-scope of this article is supporting those switching from Windoze.&lt;/p&gt;

&lt;p&gt;Towards enabling up to 5x developer-productivity by allowing developers to robustly SIMULATE the Cloud-environment on a laptop — I have the following series of articles re: M1-chipset based MacBooks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; (This) Running AWS CodeBuild locally on MacBook-M1.

&lt;ul&gt;
&lt;li&gt; Running Containers based on older &lt;code&gt;Ubuntu 20.04&lt;/code&gt; (released in the year 2020) as well as on the newer &lt;code&gt;Ubuntu 22.04&lt;/code&gt; (released in the year 2022)&lt;/li&gt;
&lt;li&gt; Running Containers based on &lt;code&gt;arm64&lt;/code&gt;-based Linux&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://dev.to/seetamraju/running-aws-glue-locally-on-a-macbook-m1-3635"&gt;Running &lt;code&gt;AWS Glue&lt;/code&gt; locally&lt;/a&gt; on MacBook-M1.
Various scenarios covered like: you do Not have “aws credentials” on your Laptop (forcing you to mock all the AWS API calls like &lt;code&gt;S3 GET&lt;/code&gt;, Glue-Catalog queries, etc..)&lt;/li&gt;
&lt;li&gt; &lt;a href="https://dev.to/seetamraju/creating-arm64aarch64-docker-images-on-a-macbook-m1-new-security-related-best-practices-4jn9"&gt;New Security-related Best-Practices&lt;/a&gt; when creating &lt;code&gt;arm64/aarch64&lt;/code&gt; Docker-Images on a MacBook-M1.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Aiming for very simple set of commands, based on bash-shell scripts --&amp;gt; to start a CodeBuild’s build locally on your MacBook-M1 laptop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You may ask&lt;/strong&gt;: Why even bother with &lt;code&gt;arm64/aarch64&lt;/code&gt; docker images, especially when we can set the following ENV-Variables and successfully emulate &lt;code&gt;x86/amd64&lt;/code&gt; chipsets on MacBook-M1?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export BUILDPLATFORM="linux/amd64"
export DOCKER_DEFAULT_PLATFORM="${BUILDPLATFORM}"
export TARGETPLATFORM="${DOCKER_DEFAULT_PLATFORM}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;My response&lt;/strong&gt;: You should aim to have your software work on &lt;code&gt;linux/arm64&lt;/code&gt; containers, which invariably is cheapest compute on cloud.&lt;/p&gt;

&lt;p&gt;Next, I have tips on how to significantly raise your productivity, in debugging/developing your &lt;code&gt;buildspec.yaml&lt;/code&gt;, even if your company denies your AWS CLI credentials.&lt;/p&gt;

&lt;p&gt;Yes, this article is based on &lt;a href="https://docs.aws.amazon.com/codebuild/latest/userguide/use-codebuild-agent.html"&gt;AWS own official Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But .. ..&lt;/p&gt;

&lt;p&gt;those AWS instructions (link above) are a very complex for anything other than &lt;code&gt;AmazonLinux2/2023&lt;/code&gt; on x86, with “gotchas” and challenges of using Ubuntu-vs-AmazonLinux on a MacBook-M1 Laptop.&lt;/p&gt;

&lt;p&gt;Based on very simple CLI-arguments, the bash-scripts offered in this article will help automatically download these images (if missing) and execute a build using the buildspec file in current-working directory.&lt;/p&gt;

&lt;h1&gt;
  
  
  Very-Advanced User: Short summary
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;You need 2 container-images are required to start a CodeBuild’s build locally on your laptop.

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;amazon/aws-codebuild-local:latest&lt;/code&gt; (This is the “&lt;em&gt;Engine&lt;/em&gt;” a.k.a. AWS-CodeBuild’s Agent/Platform on your laptop) &lt;/li&gt;
&lt;li&gt;If &lt;code&gt;AmazonLinux2/2023&lt;/code&gt; is your Build O/S: pull the image  &lt;code&gt;public.ecr.aws/codebuild/amazonlinux2-x86_64-standard:5.0&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;If Ubuntu &lt;code&gt;20.04&lt;/code&gt; or &lt;code&gt;22.04&lt;/code&gt; is your Build-env O/S, then you’ll need locally custom-build the following Docker-Images from AWS official source-code:

&lt;ul&gt;
&lt;li&gt; &lt;code&gt;aws/codebuild/standard:5.0&lt;/code&gt; &lt;/li&gt;
&lt;li&gt; &lt;code&gt;aws/codebuild/standard:7.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; No, you can NOT find these images anywhere! &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;Optionally, instead of image #2 above, if you want &lt;code&gt;aarch64&lt;/code&gt;-chipset:

&lt;ol&gt;
&lt;li&gt;then download the image:  &lt;code&gt;public.ecr.aws/codebuild/local-builds:aarch64&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;For a full list of all possible images (with Go/Java/Python/..):   &lt;code&gt;aws codebuild list-curated-environment-images&lt;/code&gt; &lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Problem Statements
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Want to use Ubuntu as the O/S for CodeBuild projects, and use it to test/debug locally on my MacBook-M1.&lt;/li&gt;
&lt;li&gt;Rather than wait 10+ minutes to find a stupid-mistake in the &lt;code&gt;buildspec.yaml&lt;/code&gt; file, can I run &lt;code&gt;buildspec.yaml&lt;/code&gt; file locally on my laptop, to test everything in it?&lt;/li&gt;
&lt;li&gt;How to proactively ensure the CodeBuild project’s build will work on all chip-architectures - without having to scramble later? How to explicitly utilize &lt;code&gt;x86_64/amd64/arm64/aarch64&lt;/code&gt; architectures locally on laptop?&lt;/li&gt;
&lt;li&gt;Company does Not allow Laptops to have AWS-Credentials (in &lt;code&gt;~/.aws/credentials&lt;/code&gt; file); My &lt;code&gt;buildspec.yaml&lt;/code&gt; file uses Secrets, Parameter-Store, etc.. as well as AWS CLI commands. Even so, how can I &lt;strong&gt;EFFICIENTLY&lt;/strong&gt; test/debug the &lt;code&gt;buildspec.yaml&lt;/code&gt; file locally on my laptop?&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Ready to use scripts
&lt;/h1&gt;

&lt;p&gt;Please note: For &lt;strong&gt;Ubuntu&lt;/strong&gt; based CodeBuild, read the &lt;strong&gt;sub-section&lt;/strong&gt; (&lt;strong&gt;below&lt;/strong&gt;) titled “&lt;em&gt;CodeBuild projects using Ubuntu&lt;/em&gt;”.&lt;/p&gt;

&lt;p&gt;I have bash-shell scripts that require just one CLI-argument (either &lt;code&gt;AmazonLinux2&lt;/code&gt; or &lt;code&gt;UBUNTU&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;To get started .. ..&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;git clone https://gitlab.com/tosarma/macbook-m1&lt;/code&gt;  

&lt;ul&gt;
&lt;li&gt; You’ll notice 2 bash-scripts in AWS-CodeBuild/  sub-folder:

&lt;ul&gt;
&lt;li&gt;“&lt;code&gt;LOCAL-aws-codebuild-runner.sh&lt;/code&gt;” and&lt;/li&gt;
&lt;li&gt;“&lt;code&gt;LOCAL-create-aws-codebuild-standard-image.sh&lt;/code&gt;” &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;On your &lt;code&gt;bash&lt;/code&gt;-terminal, run the 1st script above using the full-path to it.

&lt;ul&gt;
&lt;li&gt;FYI: The 1st script will automatically run the 2nd one as needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;FYI — Running the &lt;strong&gt;1st&lt;/strong&gt; script assumes you have a  &lt;code&gt;buildspec.yaml&lt;/code&gt; file in the current-folder.  Or a “&lt;code&gt;.yml&lt;/code&gt;” file.&lt;br&gt;
Note: If you have a &lt;strong&gt;&lt;code&gt;LOCAL&lt;/code&gt;&lt;/strong&gt;&lt;code&gt;-buildspec.yaml&lt;/code&gt; file in the current-folder, then it is used instead.&lt;br&gt;
Re: this “&lt;strong&gt;&lt;code&gt;LOCAL&lt;/code&gt;&lt;/strong&gt;-” file, immediately please read the following &lt;strong&gt;sub-section&lt;/strong&gt; “&lt;em&gt;No AWS Credentials on your laptop?&lt;/em&gt;”.&lt;/p&gt;

&lt;p&gt;Note: Only for “&lt;code&gt;AmazonLinux2&lt;/code&gt;” cli-argument, the __ FIRST __ time you run the above script it’ll take &lt;strong&gt;up to 5-minutes&lt;/strong&gt; to download about 30+/- images from AWS ECR-Repo.&lt;/p&gt;
&lt;h2&gt;
  
  
  No Bash?  Want Python instead?
&lt;/h2&gt;

&lt;p&gt;Just replace the “&lt;code&gt;.sh&lt;/code&gt;” with “&lt;code&gt;.py&lt;/code&gt;” — in the script names (above).&lt;br&gt;
And, of course, you must insert “&lt;code&gt;python3&lt;/code&gt;” at the very beginning of the CLI (this is a platform-independent advice).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;: Without the benefit of “&lt;code&gt;docker&lt;/code&gt; cli”, you get &lt;strong&gt;ZERO&lt;/strong&gt; visibility into the progress of docker-activity. This is due to use of un-friendly Docker’s Python APIs, because of which the python-code _ WILL _  _ HANG _ for very long time!&lt;/p&gt;

&lt;p&gt;To repeat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; “&lt;code&gt;LOCAL-aws-codebuild-runner.py&lt;/code&gt;” will hang with NO output, for roughly 2-to-5 minutes (depending on how much CPU and MEMORY you have allocated to the Docker-Desktop, as well as speed of your internet connection)&lt;/li&gt;
&lt;li&gt; “&lt;code&gt;LOCAL-create-aws-codebuild-standard-image.py&lt;/code&gt;” will hang with NO output .. ..

&lt;ul&gt;
&lt;li&gt; .. for &lt;strong&gt;AmazonLinux2&lt;/strong&gt; for roughly 2-to-5 minutes (depending on how much CPU and MEMORY you have allocated to the Docker-Desktop, as well as speed of your internet connection)&lt;/li&gt;
&lt;li&gt; .. for &lt;strong&gt;Ubuntu&lt;/strong&gt; (see full details below) for a minimum of 2+  &lt;u&gt;&lt;strong&gt;H O U R S&lt;/strong&gt;&lt;/u&gt; !! (If you have minimal CPU allocated for Docker-Desktop, it will as long as 4+ &lt;strong&gt;HOURS&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Important - Note these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; I only tested using &lt;code&gt;Python3.11&lt;/code&gt;; No other Python version tested.&lt;/li&gt;
&lt;li&gt; PRE-REQUISITES:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pip3 install docker&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pip3 install GitPython&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  Using Ubuntu 20.04
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: As of 2024-January, only ‘&lt;code&gt;x86_64/amd64&lt;/code&gt;’ chipset-architecture supported (on MacBook-M1) for “&lt;em&gt;Ubuntu&lt;/em&gt;” O/S.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question&lt;/strong&gt;: Who would use Ubuntu (instead of AmazonLinux) - for their CodeBuild projects?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer&lt;/strong&gt;: Ubuntu has &lt;strong&gt;great trouble-free&lt;/strong&gt; support for installing EXACT versions of software, whether Google-Chrome (for headless testing) or older versions of NodeJS or Python, etc.. ..&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: Only for “&lt;em&gt;Ubuntu&lt;/em&gt;” O/S CodeBuild builds, you _ MUST _ turn-&lt;strong&gt;OFF&lt;/strong&gt; (that is, &lt;strong&gt;un&lt;/strong&gt;-check the checkbox) as shown in screenshot below.&lt;br&gt;
Note: For fix many __ OTHER __ issues while running Docker-containers on MacBook-M1, you are REQUIRED to turn-&lt;strong&gt;ON&lt;/strong&gt; this checkbox.&lt;br&gt;
So, please pay attention to conflicting configurations (within Docker-Desktop on MacBook-M1)!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: Only for “&lt;em&gt;Ubuntu&lt;/em&gt;” O/S, the __ FIRST __ time you run the above script it’ll &lt;strong&gt;takes minimum 2 hours&lt;/strong&gt; — to re-create Ubuntu Image runtime from scratch (after downloading the AWS Source-code).&lt;br&gt;&lt;br&gt;
Why? Because, unfortunately, AWS does Not offer these ready-to-use images for Ubuntu O/S to download.&lt;/p&gt;
&lt;h3&gt;
  
  
  To get started, here are the simple commands to run!
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd &amp;lt;Your-own-project&amp;gt;

export BUILDPLATFORM=linux/amd64
export DOCKER_DEFAULT_PLATFORM="${BUILDPLATFORM}"
export TARGETPLATFORM="${DOCKER_DEFAULT_PLATFORM}"

&amp;lt;Path-to-Git-Cloned-folder&amp;gt;/AWS-CodeBuild/LOCAL-aws-codebuild-runner.sh  UBUNTU
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Let me know of any issues with the above.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DjGgUD_W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4lgwj27y8v73lav8pwx7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DjGgUD_W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4lgwj27y8v73lav8pwx7.png" alt="Image description" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Issues &amp;amp; Errors?
&lt;/h1&gt;

&lt;p&gt;See Appendix, for resolving the errors.&lt;br&gt;
Example: See Appendix sub-section titled “&lt;em&gt;Docker-Desktop settings for &lt;strong&gt;Ubuntu&lt;/strong&gt;-on-x86 images&lt;/em&gt;”.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing Chromium Headless on Ubuntu
&lt;/h2&gt;

&lt;p&gt;See &lt;code&gt;install-Chromium-latest-on-ubuntu20.04.sh&lt;/code&gt; &lt;br&gt;
under &lt;a href="https://gitlab.com/tosarma/macbook-m1/-/tree/main/AWS-CodeBuild/software-install-scripts?ref_type=heads"&gt;https://gitlab.com/tosarma/macbook-m1/-/tree/main/AWS-CodeBuild/software-install-scripts?ref_type=heads&lt;/a&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Installing Old Node.JS on Ubuntu
&lt;/h2&gt;

&lt;p&gt;Edit and run &lt;code&gt;install-NodeJS-latest-on-ubuntu20.04.sh&lt;/code&gt; &lt;br&gt;
under &lt;a href="https://gitlab.com/tosarma/macbook-m1/-/tree/main/AWS-CodeBuild/software-install-scripts?ref_type=heads"&gt;https://gitlab.com/tosarma/macbook-m1/-/tree/main/AWS-CodeBuild/software-install-scripts?ref_type=heads&lt;/a&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Installing Old Python on Ubuntu
&lt;/h2&gt;

&lt;p&gt;Edit and run &lt;code&gt;install-python-latest-on-ubuntu20.04.sh&lt;/code&gt; &lt;br&gt;
under &lt;a href="https://gitlab.com/tosarma/macbook-m1/-/tree/main/AWS-CodeBuild/software-install-scripts?ref_type=heads"&gt;https://gitlab.com/tosarma/macbook-m1/-/tree/main/AWS-CodeBuild/software-install-scripts?ref_type=heads&lt;/a&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Ubuntu 22.04
&lt;/h2&gt;

&lt;p&gt;To switch to the &lt;strong&gt;newer&lt;/strong&gt; Ubuntu &lt;code&gt;22.04&lt;/code&gt; (released in the year 2022) ..&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Edit script “&lt;code&gt;./AWS-CodeBuild/LOCAL-create-aws-codebuild-standard-image.sh&lt;/code&gt;”.&lt;/li&gt;
&lt;li&gt;UN-comment the &lt;strong&gt;line # 21&lt;/strong&gt; (to use “&lt;code&gt;aws/codebuild/standard:&lt;/code&gt;&lt;strong&gt;&lt;code&gt;7.0&lt;/code&gt;&lt;/strong&gt;”)&lt;/li&gt;
&lt;li&gt;Comment out the next &lt;strong&gt;line # 22&lt;/strong&gt;. (Disable “&lt;del&gt;&lt;code&gt;5.0&lt;/code&gt;&lt;/del&gt;”)&lt;/li&gt;
&lt;li&gt;Must run “&lt;code&gt;docker system prune --all --force —volumes&lt;/code&gt;”.&lt;/li&gt;
&lt;li&gt;Finally, follow the instructions in &lt;strong&gt;above&lt;/strong&gt; section titled “&lt;em&gt;CodeBuild projects using Ubuntu 20.04&lt;/em&gt;”&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;
  
  
  No AWS Credentials on your laptop?
&lt;/h1&gt;

&lt;p&gt;For security-reasons, many companies are denying developers the AWS-credentials for AWS-CLI use.&lt;br&gt;
If your &lt;code&gt;buildspec.yaml&lt;/code&gt; file has &lt;strong&gt;AWS-Secrets&lt;/strong&gt; (quite common!) or if it runs &lt;strong&gt;AWS-CLI&lt;/strong&gt; inside (example: to get Stack-outputs), etc .. ..&lt;br&gt;
.. then, you have a &lt;strong&gt;showstopper&lt;/strong&gt; in locally testing/debugging your &lt;code&gt;buildspec.yaml&lt;/code&gt; locally on your laptop.&lt;/p&gt;

&lt;p&gt;My best-practice is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a 2nd (new) file named “&lt;code&gt;LOCAL-buildspec.yaml&lt;/code&gt;” (as described below) + &lt;/li&gt;
&lt;li&gt;Create a 3rd (new) file called “&lt;code&gt;.env&lt;/code&gt;” file to along with it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;WARNING: Do _ NOT _ git-commit the “.env” file, as per global practices.&lt;br&gt;
Immediately add the “&lt;code&gt;.env&lt;/code&gt;” file to your “&lt;code&gt;.gitignore&lt;/code&gt;” file.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This &lt;strong&gt;new&lt;/strong&gt; “&lt;code&gt;LOCAL-buildspec.yaml&lt;/code&gt;” file will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Not have entries for Secrets and Parameter-Store entries&lt;/li&gt;
&lt;li&gt;Not have all AWS-CLI commands&lt;/li&gt;
&lt;li&gt;Not have any CDK or other commands.&lt;/li&gt;
&lt;li&gt;Must “source” the .env file, in the “Install or Pre-Build” phases as:&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;.  .env&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;NOTE: Re: the &lt;code&gt;.env&lt;/code&gt; file:-  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You will ensure the &lt;code&gt;.env&lt;/code&gt; file sets all &lt;code&gt;bash&lt;/code&gt;-variables that’ll contain the values for the Secrets, Parameter-Store entries, etc. ..&lt;/li&gt;
&lt;li&gt;You will ensure &lt;code&gt;.env&lt;/code&gt; file also provides all the values expected from the &lt;strong&gt;AWS-CLI&lt;/strong&gt; commands (that were removed).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd &amp;lt;your-own-project&amp;gt;

&amp;lt;Path-to-Git-Cloned-folder&amp;gt;/AWS-CodeBuild/LOCAL-aws-codebuild-runner.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Open Questions, Concerns and Challenges
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;WARNING: Do _ NOT _ &lt;code&gt;git commit&lt;/code&gt; the &lt;code&gt;.env&lt;/code&gt; file, as per global practices.&lt;br&gt;
Immediately add the &lt;code&gt;.env&lt;/code&gt; file to your &lt;code&gt;.gitignore&lt;/code&gt; file.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you mistakenly commit it, confess honestly and immediately to your corporate security team, and work on fixing the security vulnerability.&lt;/p&gt;

&lt;p&gt;FYI - CodeBuild for Ubuntu O/S on &lt;code&gt;aarch64&lt;/code&gt; &amp;amp; &lt;code&gt;amd64&lt;/code&gt; chipsets are Not yet supported.&lt;br&gt;
No timetables available.&lt;/p&gt;

&lt;p&gt;Note: If you mistakenly or consciously run “&lt;code&gt;docker system prune&lt;/code&gt;”, the Ubuntu O/S option will take 2+ hrs again (to re-create images from AWS official source-code)!&lt;br&gt;
Warning: Do __ Not __ rely on “save” and “import” of the docker-image.&lt;br&gt;
The Following &lt;strong&gt;failed&lt;/strong&gt; for me.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; docker save                   --output ~/aws_codebuild_standard_5.0.tar   aws/codebuild/standard:5.0  
 docker import --platform linux/x86_64  ~/aws_codebuild_standard_5.0.tar   aws/codebuild/standard:5.0 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;FYI: Above “.tar” file is typically 10GB in size and takes about 2-3 minutes to be “saved”.&lt;/p&gt;

&lt;h1&gt;
  
  
  APPENDIX
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Docker-Desktop settings for Ubuntu-on-x86 images
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FXY9kWFt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/93oi48blk0j6uioc27vh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FXY9kWFt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/93oi48blk0j6uioc27vh.png" alt="Image description" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why?&lt;br&gt;
As of 2023, per &lt;a href="https://github.com/moby/moby/issues/44578"&gt;https://github.com/moby/moby/issues/44578&lt;/a&gt; Docker-Desktop’s &lt;em&gt;containerd-integration&lt;/em&gt; can NOT interact with images that don't have the default platform.&lt;br&gt;
And .. &lt;code&gt;aarch64&lt;/code&gt; is the default-platform on MacBook-M1, while we’re seeking &lt;code&gt;amd64&lt;/code&gt; for Ubuntu.&lt;/p&gt;

&lt;h1&gt;
  
  
  Docker-Desktop settings for aarch64-chipset
&lt;/h1&gt;

&lt;p&gt;See screenshot in previous sub-section.&lt;br&gt;
Turn _ ON _ the setting titled “&lt;em&gt;Use containerd for pulling and storing images&lt;/em&gt;”!&lt;br&gt;
Yup!  Doing &lt;strong&gt;&lt;u&gt;just the opposite&lt;/u&gt;&lt;/strong&gt;!&lt;br&gt;
I can’t help explain this crazy conflicting instructions.&lt;br&gt;
As of 2023, this is a Docker-on-MacBook issue.&lt;/p&gt;

&lt;h1&gt;
  
  
  Running out of Disk-space or Memory?
&lt;/h1&gt;

&lt;p&gt;Screenshot below shows the recommended “high” settings.&lt;br&gt;
After building images, you can reduce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; “CPU” can be lowered to “2”.&lt;/li&gt;
&lt;li&gt;“Memory” can be lowered to “4GB”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FYI only - To run on a MacBook-M1, many &lt;code&gt;amd64&lt;/code&gt; emulated containers like &lt;code&gt;Neo4j v4.x&lt;/code&gt; will frequently fail, unless you provide Docker with a minimum of 5+ cpus and 8GB of RAM!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y1ur0qRw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gw83ftjg90tb61gc07ub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y1ur0qRw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gw83ftjg90tb61gc07ub.png" alt="Image description" width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>macbook</category>
      <category>aws</category>
      <category>codebuild</category>
      <category>docker</category>
    </item>
    <item>
      <title>Creating ARM64/Aarch64 Docker-Images on a MacBook-M1 - New Security-related Best-Practices</title>
      <dc:creator>Sarma</dc:creator>
      <pubDate>Mon, 08 Jan 2024 03:12:11 +0000</pubDate>
      <link>https://dev.to/seetamraju/creating-arm64aarch64-docker-images-on-a-macbook-m1-new-security-related-best-practices-4jn9</link>
      <guid>https://dev.to/seetamraju/creating-arm64aarch64-docker-images-on-a-macbook-m1-new-security-related-best-practices-4jn9</guid>
      <description>&lt;p&gt;by Udaybhaskar Sarma Seetamraju&lt;br&gt;
&lt;a href="mailto:ToSarma@gmail.com"&gt;ToSarma@gmail.com&lt;/a&gt;&lt;br&gt;
Dec 31 2023&lt;/p&gt;
&lt;h1&gt;
  
  
  Highest-level Context
&lt;/h1&gt;

&lt;p&gt;If you are into “&lt;strong&gt;Shift-Left&lt;/strong&gt;” (whether re: Testing, Security, or Replicating-problems-on-developer-laptop, etc ..), then this article is for you.&lt;/p&gt;

&lt;p&gt;For the very first time that you switch to an M1-chipset based MacBooks (from intel-chip based MacBooks) .. Productivity is significantly impacted when doing development/testing/troubleshooting “locally” on your laptop.  Out-of-scope of this article is supporting those switching from Windoze.&lt;/p&gt;

&lt;p&gt;Towards enabling up to 5x developer-productivity by allowing developers to robustly SIMULATE the Cloud-environment on a laptop — I have the following series of articles re: M1-chipset based MacBooks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;a href="https://dev.to/seetamraju/running-aws-codebuild-locally-on-a-macbook-m1-9eo"&gt;Running AWS CodeBuild locally&lt;/a&gt; on MacBook-M1.

&lt;ul&gt;
&lt;li&gt; Running Containers based on older &lt;code&gt;Ubuntu 20.04&lt;/code&gt; (released in the year 2020) as well as on the newer &lt;code&gt;Ubuntu 22.04&lt;/code&gt; (released in the year 2022)&lt;/li&gt;
&lt;li&gt; Running Containers based on &lt;code&gt;arm64&lt;/code&gt;-based Linux&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://dev.to/seetamraju/running-aws-glue-locally-on-a-macbook-m1-3635"&gt;Running &lt;code&gt;AWS Glue&lt;/code&gt; locally&lt;/a&gt; on MacBook-M1.
Various scenarios covered like: you do Not have “aws credentials” on your Laptop (forcing you to mock all the AWS API calls like &lt;code&gt;S3 GET&lt;/code&gt;, Glue-Catalog queries, etc..)&lt;/li&gt;
&lt;li&gt; (This) New Security-related Best-Practices when creating &lt;code&gt;arm64/aarch64&lt;/code&gt; Docker-Images on a MacBook-M1.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;You’ve been issued a MacBook-M1 laptop (by your own choice or otherwise).  You may have prior experience working on Intel-based MacBooks; but, that is of little help once you start “local” Container-based development on it.  Basic commands like “&lt;code&gt;npm&lt;/code&gt;” and “&lt;code&gt;maven/gradle&lt;/code&gt;” as well as “&lt;code&gt;curl/wget&lt;/code&gt;” stop working for &lt;strong&gt;local&lt;/strong&gt; &lt;code&gt;Dockerfile&lt;/code&gt; builds, with the weirdest in-decipherable errors that leave you and the rest of your development simply cannot figure out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You may ask&lt;/strong&gt;: Why even bother with &lt;code&gt;arm64/aarch64&lt;/code&gt; docker images, especially when we can set the following ENV-Variables and successfully emulate &lt;code&gt;x86/amd64&lt;/code&gt; chipsets on MacBook-M1?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export DOCKERPLATFORM="linux/amd64"
export DOCKER_DEFAULT_PLATFORM="${DOCKERPLATFORM}"
export TARGETPLATFORM="${DOCKER_DEFAULT_PLATFORM}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;My response&lt;/strong&gt;: Sooner or later you’ll hit a situation like this -&amp;gt; One single &lt;code&gt;Neo4j ver4.x&lt;/code&gt; container in &lt;code&gt;amd64&lt;/code&gt;-emulation mode takes up 50% of cpus + 8GB/50% of RAM on your MacBook-M1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefit #1&lt;/strong&gt;: If you’d like to have your software work on &lt;code&gt;linux/arm64&lt;/code&gt;, which invariably is cheapest compute on cloud.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefit #2&lt;/strong&gt;: In addition, this document hopefully will bring some significant changes to the way you look at “free software” (as mostly un-trustworthy). Hope this will help you to &lt;strong&gt;be in harmony with MacOS&lt;/strong&gt; and thereby &lt;strong&gt;avoid fighting&lt;/strong&gt; with the Corporate Security-team (so that you can efficiently continue with your software development).&lt;/p&gt;

&lt;h1&gt;
  
  
  Advanced-User: Short summary
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt; Use your Laptop’s browser (Chrome, ideally) and download the entire “Cert-Chain” of SSL-Public-Certs for maven.apache.org and for npm.org or .. whatever url/website is failing on your MacBook-M1.&lt;/li&gt;
&lt;li&gt;Store all these downloaded SSL-certs into a new folder in your project. &lt;/li&gt;
&lt;li&gt;See also: “&lt;em&gt;should you store these in Git?&lt;/em&gt;” in sub-section below (preceding the Appendix).&lt;/li&gt;
&lt;li&gt;For Java, use “&lt;code&gt;keytool -importcert&lt;/code&gt;” commands to import these (within the &lt;code&gt;Dockerfile&lt;/code&gt;). For NodeJS/npm, use “&lt;code&gt;npm config set cafile&lt;/code&gt;” command to import the &lt;strong&gt;“unified” CER&lt;/strong&gt; file (within the &lt;code&gt;Dockerfile&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Problem Statement: What you experience on your MacBook-M1
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;curl&lt;/code&gt; commands (within &lt;code&gt;Dockerfile&lt;/code&gt;) fail during “&lt;code&gt;docker build&lt;/code&gt;” commands on MacBook-M1.&lt;/li&gt;
&lt;li&gt;“&lt;code&gt;mvn&lt;/code&gt;” Maven commands (within &lt;code&gt;Dockerfile&lt;/code&gt;) fail on MacBook-M1 — see sample error below.&lt;/li&gt;
&lt;li&gt;“&lt;code&gt;npm&lt;/code&gt;” commands (within &lt;code&gt;Dockerfile&lt;/code&gt;) fail on MacBook-M1 — see sample error below.&lt;/li&gt;
&lt;li&gt;“&lt;code&gt;dockerfile-maven-plugin&lt;/code&gt;” uses a runtime based on &lt;code&gt;x86&lt;/code&gt; architecture and will NOT run on Apple M1 (&lt;code&gt;aarch64&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sample “mvn/maven” &lt;strong&gt;ERROR&lt;/strong&gt; (for a &lt;code&gt;spring-boot&lt;/code&gt; app):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Non-resolvable parent POM for groupId.subgroupId.myapp:myapp:0.0.1:
Could not transfer artifact org.springframework.boot:spring-boot-starter-parent:pom:3.0.2
from/to central (https://repo.maven.apache.org/maven2):
transfer failed for https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/3.0.2/spring-boot-starter-parent-3.0.2.pom
and 'parent.relativePath' points at wrong local POM @ line __,
column 13: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
..
Root Cause ERROR: UnsafeLegacyRenegotiation option for SSL connections
 is now disabled by default in OpenSSL 3.0
..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample “npm” error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qemu: uncaught target signal 5 (Trace/breakpoint trap) - core dumped
Trace/breakpoint trap
ERROR: executor failed running [/bin/sh -c npm install --production]: exit code: 133
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  What is the underlying root-cause?
&lt;/h1&gt;

&lt;p&gt;MacBook-M1-chip based laptops use the latest Apple MacOS.&lt;br&gt;
Latest MacOS uses &lt;code&gt;SSL v3.x&lt;/code&gt;.&lt;br&gt;
Latest MacOS &lt;strong&gt;exclusively relies&lt;/strong&gt; on &lt;code&gt;SSL-v3.x&lt;/code&gt; (if you rely on Xcode’s tools and on home-brew).&lt;/p&gt;

&lt;p&gt;FYI - &lt;code&gt;SSL-v3&lt;/code&gt; is officially renamed to “&lt;code&gt;TLS v1.2&lt;/code&gt;”.&lt;br&gt;
It is the latest and greatest and apparently, much much much more secure (than &lt;code&gt;SSL-v2.x&lt;/code&gt; which is now considered legacy and a security-risk).&lt;br&gt;
For those who are a tiny bit curious about why &lt;code&gt;SSL-v3.0/TLS-v1.2&lt;/code&gt; is a big-deal, take a quick look at Amazon’s decision to implement their own custom TINY-version of TLS - as published in “&lt;a href="https://d1.awsstatic.com/Security/pdfs/Continuous_Formal_Verification_Of_Amazon_s2n.pdf" rel="noopener noreferrer"&gt;Continuous Formal Verification of Amazon s2n&lt;/a&gt;”&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SSL v2.x&lt;/code&gt; has been around for so long, and .. “most” (in quotes) of its &lt;em&gt;Ciphers&lt;/em&gt; it supports are considered legacy.&lt;br&gt;
Translating that into developer language .. ..&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AlpineLinux.org&lt;/code&gt; website uses “legacy” SSL-Cert CIPHERS.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;maven.apache.org&lt;/code&gt; — same issue.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm.org&lt;/code&gt; — same issue.&lt;/li&gt;
&lt;li&gt;.. &lt;em&gt;etcetera&lt;/em&gt; .. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bottomline .. ..&lt;br&gt;&lt;br&gt;
Since Docker relies on the laptop’s O/S for all Cryptography-related activities, Docker &lt;strong&gt;is missing support for&lt;/strong&gt; the legacy SSL-v2.x on the MacBook-M1.&lt;/p&gt;
&lt;h1&gt;
  
  
  Really BAD-idea(s) &amp;amp; Hit-or-Miss workaround(s)
&lt;/h1&gt;

&lt;p&gt;(A) Choose “mirrors” that use latest SSL-Cert ciphers.&lt;br&gt;&lt;br&gt;
Example: switch to Alpine-MIRROR: &lt;a href="https://mirror.math.princeton.edu/pub/alpinelinux/v3.17/" rel="noopener noreferrer"&gt;https://mirror.math.princeton.edu/pub/alpinelinux/v3.17/&lt;/a&gt; &lt;br&gt;&lt;br&gt;
Unfortunately, it’s very hard to find “good” mirrors (that use SSL-v3.x/TLS-v1.2).&lt;/p&gt;

&lt;p&gt;(B) Get rid of encryption-in-motion! ugh!&lt;br&gt;&lt;br&gt;
Example: replace “&lt;code&gt;httpS&lt;/code&gt;” with plain “&lt;code&gt;http&lt;/code&gt;” within “Dockerfile”.&lt;br&gt;&lt;br&gt;
&lt;del&gt;https&lt;/del&gt; &lt;code&gt;curl http://archive.apache.org/dist/tomcat/tomcat-"$TOMCAT_MAJOR"/v"$TOMCAT_VERSION"/bin/apache-tomcat-"$TOMCAT_VERSION".tar.gz&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Robust and Permanent Fixes
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Step 1: Download the SSL-Certs - the complete “Hierarchy”
&lt;/h2&gt;

&lt;p&gt;Since I have many easy-to-follow screenshots re: this, and since I want to keep this article “short” .. ..&lt;br&gt;&lt;br&gt;
.. Please jump to the only Appendix at the end of this article.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2: For Java Maven
&lt;/h2&gt;

&lt;p&gt;Inside Dockerfile, run the following commands.&lt;br&gt;&lt;br&gt;
Important for Java-only: 1st import the TOP-MOST CA-Cert; &lt;br&gt;
Then, finally, import (bottommost in chain) web-site SSL-Cert.&lt;/p&gt;

&lt;p&gt;The “&lt;code&gt;if&lt;/code&gt;”  condition (below) is just a suggestion - so that the keytool is ONLY run for MacBook-M1 chipset.&lt;br&gt;
Keep it, if you’d like to have your software work on &lt;code&gt;linux/arm64&lt;/code&gt;, which invariably is cheaper compute on AWS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RUN if [ "${TARGETPLATFORM}" == "linux/aarch64" ] || [ "${TARGETPLATFORM}" == "linux/arm64" ]; then \
      keytool -importcert -noprompt -trustcacerts \
        -cacerts -storepass changeit   -keystore "${JAVA_HOME}/lib/security/cacerts"   \
        -alias ROOT-1A               -file "${APP_SRC_FOLDER}/etc/ROOT-1A.cer"; \
      keytool -importcert -noprompt -trustcacerts \
        -cacerts -storepass changeit   -keystore "${JAVA_HOME}/lib/security/cacerts"   \
        -alias SSLCA-1A.cer          -file "${APP_SRC_FOLDER}/etc/SSLCA-1A.cer";   \
      keytool -importcert -noprompt -trustcacerts \
                 -storepass changeit   -keystore "${JAVA_HOME}/lib/security/cacerts"   \
        -alias org.apache.maven.repo -file "${APP_SRC_FOLDER}/etc/org.apache.maven.repo.cer"; \
    fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Java’s KeyTool command: &lt;a href="https://docs.oracle.com/en/java/javase/17/docs/specs/man/keytool.htm" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/java/javase/17/docs/specs/man/keytool.htm&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: For npm
&lt;/h2&gt;

&lt;p&gt;ISSUE: We have multiple Cert-files.  But, “&lt;code&gt;npm&lt;/code&gt;” can only accept ONE SINGLE file.&lt;br&gt;&lt;br&gt;
SOLUTION:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Combine multiple Cert-files into a single file.&lt;/li&gt;
&lt;li&gt;Here’s &lt;code&gt;bash&lt;/code&gt;-commands for that.&lt;/li&gt;
&lt;li&gt;Note: The files should be listed in the ORDER in which they were downloaded - as shown in this article.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;FILES_IN_PROPER_SEQ=(&lt;/code&gt;&lt;del&gt;&lt;code&gt;nodejs.org.cer&lt;/code&gt;&lt;/del&gt;&lt;br&gt;
        &lt;del&gt;&lt;code&gt;Cloudflare-ECC-CA-3.cer&lt;/code&gt;&lt;/del&gt;&lt;br&gt;
        &lt;del&gt;&lt;code&gt;LEVEL2-ROOTCA-1A.cer&lt;/code&gt;&lt;/del&gt;  &lt;del&gt;&lt;code&gt;TOPMOST-ROOT-1A.cer&lt;/code&gt;&lt;/del&gt;&lt;br&gt;
&lt;code&gt;)&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TMPFILE="./etc/my-unified-certs.cer"
echo '' &amp;gt; "${TMPFILE11}"                  ### Empty out the file (if it exists)

for f in ${FILES_IN_PROPER_SEQ[@]}; do
    CERTFILE="${CERTS_FOLDER}/${f}"
    if [ ! -f "${CERTFILE}" ]; then
        echo "ERROR: File '${CERTFILE}' does not exist !"
        exit 9
    fi
    cat "${CERTFILE}" &amp;gt;&amp;gt; "${TMPFILE11}"
    echo '' &amp;gt;&amp;gt; "${TMPFILE11}"
done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NEXT: Inside &lt;code&gt;Dockerfile&lt;/code&gt;, run the following commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY      /usr/local/share/ca-certificates/.

RUN  if [ "${TARGETPLATFORM}" == "linux/aarch64" ] || [ "${TARGETPLATFORM}" == "linux/arm64" ]; then \
        npm config set cafile   ${APP_SRC_FOLDER}/etc/my-unified-certs.cer  \
     fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 (Optional) - &lt;code&gt;dockerfile-maven-plugin&lt;/code&gt; won't run on Apple M1-chip
&lt;/h2&gt;

&lt;p&gt;Try official “&lt;em&gt;substitute&lt;/em&gt;” plugins like &lt;a href="https://github.com/eclipse/jkube" rel="noopener noreferrer"&gt;Eclipse JKube&lt;/a&gt; plugin: &lt;a href="https://www.eclipse.org/jkube/docs/kubernetes-maven-plugin" rel="noopener noreferrer"&gt;Kubernetes Maven Plugin&lt;/a&gt; or &lt;a href="https://www.eclipse.org/jkube/docs/openshift-maven-plugin" rel="noopener noreferrer"&gt;OpenShift Maven&lt;/a&gt; Plugin . Refer to  &lt;a href="https://www.eclipse.org/jkube/docs/migration-guide/" rel="noopener noreferrer"&gt;Migration Guide&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h1&gt;
  
  
  Open Questions, Concerns and Challenges
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Should you store these SSL-Public-Certs in Git?
&lt;/h2&gt;

&lt;p&gt;Risks as viewed by Enterprise Security Team(s) and my responses.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Risk&lt;/strong&gt;: As a broad/generic statement, Certs and Secrets should Not be put into Git.

&lt;ul&gt;
&lt;li&gt; &lt;strong&gt;Counter-Argument #1&lt;/strong&gt;: These are PUBLIC-Key SSL-Certs.  Meant for the whole world to know (or have a copy)! &lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Counter-Argument #2&lt;/strong&gt;: By checking them into Git, perhaps .. .. Security will know what Apps are impacted if the corresponding website is ever hijacked, or the CA-Root is compromised; Security can then alert the developers - to find a workaround (or, at least pause all Builds temporarily).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Risk&lt;/strong&gt;: Not a good idea to download + store the CA-Root and the Intermediate-Root Certs.

&lt;ul&gt;
&lt;li&gt; &lt;strong&gt;Counter-Argument - Benefit&lt;/strong&gt;: So that we do Not have to “blindly” trust just one “standalone” certificate. This significantly reduces chances that Laptop will Not be downloading malware.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Challenge&lt;/strong&gt;: This is a Laptop-issue and Not a CI/CD or DevOps issue.   Nor related to application-design. So, why is this part of the codebase?

&lt;ul&gt;
&lt;li&gt; Wham!&lt;/li&gt;
&lt;li&gt; KO’ed.&lt;/li&gt;
&lt;li&gt; Hard to have a counter-argument.&lt;/li&gt;
&lt;li&gt; But here’s a pathetic “excuse”!!

&lt;ul&gt;
&lt;li&gt; There are “developers” and then there are “developers”.&lt;/li&gt;
&lt;li&gt; You know EXACTLY what I mean.&lt;/li&gt;
&lt;li&gt; Putting this in the Git-repo makes life convenient for the leads and for the senior members of any development team.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  APPENDIX
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Step 1: Download the SSL-Certs - the complete “&lt;strong&gt;Hierarchy&lt;/strong&gt;”
&lt;/h2&gt;

&lt;p&gt;Note: The screenshots are for “&lt;code&gt;maven.apache.org&lt;/code&gt;".&lt;br&gt;
Note: Please repeat the following for the website/url that fails to download on your MacBook-M1.&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%2Fz4clcyp9vjnqfnvmvvuc.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%2Fz4clcyp9vjnqfnvmvvuc.png" alt="Step 1" width="800" height="288"&gt;&lt;/a&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%2Fdtxzq4om7a4xkqqdoof9.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%2Fdtxzq4om7a4xkqqdoof9.png" alt="Step 2" width="800" height="393"&gt;&lt;/a&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%2Fet3ft5h83ryumsh8iaq6.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%2Fet3ft5h83ryumsh8iaq6.png" alt="Step 3" width="800" height="717"&gt;&lt;/a&gt;&lt;br&gt;
Step 3 screenshot somehow not rendering on browsers.  It shows how to go to the "Details" tab of "&lt;em&gt;Certificate Viewer: *.apache.org&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%2F7k4x6mbo1owh8qzub9nf.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%2F7k4x6mbo1owh8qzub9nf.png" alt="Step 4a &amp;amp; 4b" width="800" height="965"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now repeat steps &lt;code&gt;4.a&lt;/code&gt; and &lt;code&gt;4.b&lt;/code&gt; (steps shown above) .. &lt;br&gt;
.. for the two OTHER entries in the “Certificate Hierarchy” (as indicated below).&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%2Fzysmogu2bwd5fpkjrpk1.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%2Fzysmogu2bwd5fpkjrpk1.png" alt="Steps 5 &amp;amp; 6" width="800" height="985"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;/ End of Article.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Sophisticated use-case of AWS CDK that leverages AWS SDK</title>
      <dc:creator>Sarma</dc:creator>
      <pubDate>Wed, 01 Nov 2023 02:06:36 +0000</pubDate>
      <link>https://dev.to/seetamraju/sophisticated-use-case-of-aws-cdk-that-leverages-aws-sdk-3bmg</link>
      <guid>https://dev.to/seetamraju/sophisticated-use-case-of-aws-cdk-that-leverages-aws-sdk-3bmg</guid>
      <description>&lt;p&gt;by Udaybhaskar Sarma Seetamraju (ToSarma gmail)&lt;br&gt;
Oct 16 2023&lt;/p&gt;
&lt;h2&gt;
  
  
  One sentence summary
&lt;/h2&gt;

&lt;p&gt;A real-world sophisticated use-case exists for leveraging AWS SDK within AWS CDK code, when deploying cloud-native Non-Kubernetes solutions, within Enterprise-environments where IP-address space is a major constraint.&lt;/p&gt;
&lt;h2&gt;
  
  
  One Paragraph Summary
&lt;/h2&gt;

&lt;p&gt;For those of us, who do Not work for the leading-edge tech-companies (which is the overwhelming majority of the Fortune 1000), private IP addresses are a fact. That is, we face serious constraints in how many VPCs and AWS Accounts can exist. Also, frequently due to lack of an &lt;a href="https://efficientip.com/glossary/what-is-ipam/"&gt;IPAM system&lt;/a&gt;, the ability to automatically create and dispose AWS Accounts is missing.  In such Enterprise contexts, as shown in this article, using AWS SDK (within AWS CDK code) leads to far simpler and far more robust solutions, specifically when deploying cloud-native Non-Kubernetes solutions. &lt;/p&gt;
&lt;h2&gt;
  
  
  Focus Areas &amp;amp; Keywords
&lt;/h2&gt;

&lt;p&gt;DevOps, Enterprise Environments, “Dirty” VPCs, AWS CDK, AWS SDK, CLI Profiles, “Everything is Disposable”.&lt;/p&gt;
&lt;h1&gt;
  
  
  The main article
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HWYOIJv2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5s22s39qa5uwu281g0fi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HWYOIJv2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5s22s39qa5uwu281g0fi.png" alt="HighQuality" width="800" height="751"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Enterprise Context: A unwavering focus on Production
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;My Philosophical “True North”&lt;/em&gt;&lt;/strong&gt;: Regarding AWS Resources, my perspective is: AWS SDK is for &lt;em&gt;obtaining details on what already exists&lt;/em&gt;, whereas AWS CDK (incl. Cloudformation) is about &lt;em&gt;what we need to create&lt;/em&gt;.  In other words, I personally do Not use AWS SDK to create new AWS Resources, nor do I “mildly” manipulate any pre-existing AWS Resources.  FWIW: CDK itself relies on SDK.&lt;/p&gt;

&lt;p&gt;I belong to the group of people who do Not like Cloudformation in which each resource has a “&lt;code&gt;Condition:&lt;/code&gt;”.  The best practice (of maintainability / supportability) is to have Cloudformation-templates (CFT) that simply &amp;amp; clearly tell you what was deployed; It’s easy to predict/understand what will happen if you re-deploy.  In many industries, Cloudformation-templates, Stack-drifts and Change-Sets are like god’s gifts, but that is out of scope for this article.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;“Corporate Life”&lt;/em&gt;&lt;/strong&gt;: Commonly enough within Enterprise environments, either there is a limited ability, or there is an outright ban on the ability to create “fresh new” VPCs and/or AWS Accounts.  An even more important nuance is, this limitation/absence can be quite severe within Production environment/VPCs, even as developers may experience “freedom” within sandbox/PoC/Development/Integration environments.&lt;/p&gt;

&lt;p&gt;Per the best practice for designing solutions, we must have all identical environments. So, developers should “copy/simulate” the &lt;em&gt;constraints of Production&lt;/em&gt; into all the Non-prod environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Use-Case / Scenario&lt;/em&gt;&lt;/strong&gt;: This article is specific to one specific use-case, where CDK-based deployment is done manually (via “&lt;code&gt;cdk&lt;/code&gt;” CLI).  Personally, I experience this use-case early in every project, when the deployment-architecture diagram (onto AWS) is constantly in flux.  The iterative approach using the CI/CD pipeline to improve your CDK is too horribly slow, un-acceptably inefficient and error-prone.&lt;/p&gt;

&lt;p&gt;For any of my projects, for 3 specific environments (namely &lt;em&gt;sandbox&lt;/em&gt;, &lt;em&gt;PoC&lt;/em&gt; and &lt;em&gt;development&lt;/em&gt; environments), I rely on &lt;strong&gt;&lt;u&gt;manual&lt;/u&gt;&lt;/strong&gt; CDK-CLI based deployments. &lt;/p&gt;
&lt;h2&gt;
  
  
  The core security needs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sW8juh7H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8w98pgyg4khknu1xrlu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sW8juh7H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8w98pgyg4khknu1xrlu.png" alt="SecurityHazard" width="800" height="798"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; CDK offers a great advantage within DevSecOps pipelines, something we should all take FULL advantage of. Via CDK-code, we can &lt;em&gt;very easily ensure all&lt;/em&gt; IAM Roles and Network-security components are also covered robustly, for each component of overall solution.&lt;/li&gt;
&lt;li&gt;The example in this article focuses solely on AWS Security Groups (&lt;strong&gt;&lt;u&gt;SGs&lt;/u&gt;&lt;/strong&gt;), since use-cases to &lt;strong&gt;&lt;u&gt;share at least one SG&lt;/u&gt;&lt;/strong&gt; (across various components of a solution) is a common enough.  More details on this is throughout this article. Note: Never share IAM Roles across components of a solution.&lt;/li&gt;
&lt;li&gt;In the early release of a medium/large application, it is very common to have an “&lt;strong&gt;Application-wide&lt;/strong&gt;” &lt;strong&gt;Security Group&lt;/strong&gt; (“&lt;em&gt;AppWide-SG&lt;/em&gt;”).  This is a good best-practice to remember, as it forces developers to start day-one, in strictly using SGs to limit access to every single component.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What is a Dirty VPC?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_Je0rrE0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p6mm63qrrm5wqbtdw1ea.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_Je0rrE0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p6mm63qrrm5wqbtdw1ea.jpg" alt="DirtyWorkArea" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The concept of a “Dirty VPC” is simply an implication of the Enterprise constraint that “&lt;em&gt;One is restricted in creating new VPCs within DevSecOps pipelines&lt;/em&gt;”&lt;/li&gt;
&lt;li&gt;The dictionary word “&lt;em&gt;Dirty&lt;/em&gt;” has a negative meaning.  But, for this article, we will interpret that word to mean that the VPC already has one of more desired AWS resources; in this example, Security-Group(s).&lt;/li&gt;
&lt;li&gt;I love the philosophical approach off "&lt;em&gt;Everything is Disposable&lt;/em&gt;". I practice it a lot, including the use of cloud IDEs. But, “Dirty VPCs” are the proverbial “&lt;em&gt;immovable object&lt;/em&gt;” within Enterprise environments.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Some foundational facts
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OK1BrvlT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8txle0xgs5of2jp75kxm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OK1BrvlT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8txle0xgs5of2jp75kxm.png" alt="OldManuscript" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FYI: Per &lt;a href="https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_credentials.html#default-credential-chain"&gt;AWS Official documentation&lt;/a&gt;, AWS SDK authenticates (a.k.a. &lt;em&gt;&lt;strong&gt;AuthT&lt;/strong&gt;&lt;/em&gt;) based on one of the following 3 mechanisms:

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;, &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;, and &lt;code&gt;AWS_SESSION_TOKEN&lt;/code&gt; environment variables.&lt;/li&gt;
&lt;li&gt;Based on “&lt;code&gt;--profile&lt;/code&gt;” CLI arguments.&lt;/li&gt;
&lt;li&gt;Based on “&lt;code&gt;default&lt;/code&gt;” AWS Profile in &lt;code&gt;~/.credentials&lt;/code&gt; file.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;FYI: Similarly, &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/environments.html"&gt;AWS CDK relies on&lt;/a&gt; the above 3 for &lt;em&gt;AuthT&lt;/em&gt; a.k.a. &lt;em&gt;authen*&lt;em&gt;T&lt;/em&gt;*ication&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;FYI: Similarly, re: the “&lt;code&gt;Region&lt;/code&gt;”, both AWS CDK &amp;amp; AWS SDK determine it either based on environment variables, or “&lt;code&gt;--region&lt;/code&gt;” CLI-arg or on the region specified within “&lt;code&gt;default&lt;/code&gt;” AWS Profile in &lt;code&gt;~/.credentials&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Note&lt;/em&gt;: In an Enterprise work-environment, due to pervasive use of &lt;code&gt;SSO&lt;/code&gt; (Single-SignOn), you are highly likely to be using the “&lt;code&gt;--profile&lt;/code&gt;” cli-arg.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  DRY (Don’t Repeat Yourself) Problem
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5LvQ0YxB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ywp2thts6rmon6ygnvoh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5LvQ0YxB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ywp2thts6rmon6ygnvoh.png" alt="DRY" width="357" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;&lt;u&gt;Best Practice&lt;/u&gt;&lt;/em&gt;: For &lt;em&gt;sandbox/PoC/development&lt;/em&gt; environments, exclusively for &lt;strong&gt;&lt;u&gt;manual&lt;/u&gt;&lt;/strong&gt; deployments, CDK best practices suggest that you use “&lt;code&gt;--profile&lt;/code&gt;” CLI-arg (as well as the “&lt;code&gt;—region&lt;/code&gt;” CLI-arg. In contrast, for &lt;strong&gt;all other&lt;/strong&gt; environments, especially within DevSecOps pipelines, you should be &lt;em&gt;exclusively rely&lt;/em&gt; on &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;, &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;, and &lt;code&gt;AWS_SESSION_TOKEN&lt;/code&gt; environment variables.  CDK’s &amp;amp; SDK’s best-practices suggests the use of “&lt;code&gt;--profile&lt;/code&gt;” CLI-arg and the “&lt;code&gt;--Region&lt;/code&gt;” CLI-arg (instead of using “&lt;code&gt;default&lt;/code&gt;” AWS Profile). &lt;/li&gt;
&lt;li&gt;
&lt;u&gt;&lt;em&gt;Limitation&lt;/em&gt;&lt;/u&gt;: Per the AWS Support-case 13701110271, dated Sept 2023, AWS confirmed that CDK uses AWS SDK underneath, but, unfortunately, we can Not get “access” to the SDK. That implies that our code &lt;strong&gt;&lt;u&gt;must explicitly initialize AWS SDK, by providing it&lt;/u&gt;&lt;/strong&gt; the credentials.&lt;/li&gt;
&lt;li&gt;
&lt;u&gt;&lt;em&gt;The Issue&lt;/em&gt;&lt;/u&gt;: the CDK will Not share details on how it successfully-authenticated -- whether via Env-Vars, or via the “&lt;code&gt;--profile&lt;/code&gt;” CLI-arg, or via the “&lt;code&gt;default&lt;/code&gt;” AWS Profile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;u&gt;&lt;em&gt;The PROBLEM&lt;/em&gt;&lt;/u&gt;&lt;/strong&gt;: We have a “&lt;a href="https://en.wikipedia.org/wiki/Don't_repeat_yourself"&gt;DRY problem&lt;/a&gt;”, when we use “&lt;code&gt;--profile&lt;/code&gt;” CLI-arg and the “&lt;code&gt;--Region&lt;/code&gt;” CLI-arg (instead of “&lt;code&gt;default&lt;/code&gt;” AWS Profile); AWS CDK gets that information, but Not the AWS SDK.   So, we need to “copy/clone” that information and send it to AWS SDK too (for it to &lt;em&gt;AuthT&lt;/em&gt; too). See code-snippet #2 below, for the implementation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The official Workaround: AWS-SDK AuthT
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SzSruLXC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ad1po6x13gvl2zy09tl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SzSruLXC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ad1po6x13gvl2zy09tl.png" alt="SnailOnRocket" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within my CDK code, I mandate 2 additional &lt;em&gt;CLI-arguments&lt;/em&gt; as: “&lt;code&gt;--context AWSProfile=...&lt;/code&gt;” and  “&lt;code&gt;--context AWSRegion=...&lt;/code&gt;” To enforce these additional &lt;em&gt;cli-args&lt;/em&gt;, use &lt;strong&gt;code-snippet #1&lt;/strong&gt; (see below).&lt;br&gt;
FYI: The above makes your command for manual CDK-deployment look like:-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cdk deploy|synth \
     --profile ${AWSPROFILE} \
     --region ${AWSREGION} \
     --context AWSProfile=${AWSPROFILE} \
     --context AWSRegion=${AWSREGION}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Per the AWS Support-case 13701110271, dated Sept 2023, AWS FYI: I got &lt;strong&gt;2023-specific&lt;/strong&gt; confirmation on what I’m recommending within this article: “&lt;em&gt;What you are doing with the context file is the best approach to this&lt;/em&gt;.”  Please do Not assume this remains true for 2025 &amp;amp; beyond.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Snippet #1 - “&lt;code&gt;cdk&lt;/code&gt;” CLI’s mandatory CLI args
&lt;/h2&gt;

&lt;p&gt;Within your CDK project, please copy these lines within the only file: “&lt;code&gt;./bin/*.ts&lt;/code&gt;”, ideally preceding the lines starting with “&lt;code&gt;new Stack(app, .. {&lt;/code&gt;”.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const myAWSProfile = scope.node.tryGetContext('AWSProfile');

if ( ! AWSProfile ) {
    console.error(`!! FATAL-ERROR: CLI-args are missing:--&amp;gt;      --context AWSProfile=....\n\nExiting ...`);
    process.exit(1);
}

const myAWSRegion = app.node.tryGetContext('AWSRegion');

if ( ! AWSRegion ) {
    console.error(`!! FATAL-ERROR: CLI-args are missing:--&amp;gt;      --context AWSRegion =....\n\nExiting ...`);
    process.exit(1);
}

// OPTIONAL CLI-arg
const isDirtyVPC_cliarg :string | undefined = scope.node.tryGetContext('dirtyVPC');
const isDirtyVPC :boolean = (isDirtyVPC_cliarg != undefined) &amp;amp;&amp;amp; (isDirtyVPC_cliarg !== "false");
if ( isDirtyVPC ) {
    console.log(`Ok! Deploying to a "dirty" VPC that may already have certain SecurityGroups!`);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Code Snippet #2 - AWS-SDK: Authenticating into AWS-API
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as EC2 from ‘aws-cdk-lib/aws-ec2';
..
..
  const aws_sdk_credentials = {
      credentials: fromIni({
          profile: myAWSProfile,
      }),
      // region: process.env.CDK_DEPLOY_REGION || process.env.CDK_DEFAULT_REGION,
      region: myAWSRegion,
  };

  const stsclient = new STSClient(aws_sdk_credentials);
  const stscommand = new GetCallerIdentityCommand({});
  const stsresponse = stsclient.send(stscommand).then((data) =&amp;gt; {
     const AWSProfile_derived = data.Arn?.split('/')[1] || “UnknownProfile1";
     console.log(`Sanity-Check: AWSProfile in ARN = ${AWSProfile_derived}`);
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to keep your CFT simple &amp;amp; highly maintainable
&lt;/h2&gt;

&lt;p&gt;This article wouldn’t be complete without ready-to-use CDK code-snippets, to conditionally/optionally generate resources within your Cloudformation-Template (CFT), after AWS-SDK calls are used to determine those conditions.&lt;/p&gt;

&lt;p&gt;In this example (code snippet #3), we use AWS-SDK calls to determine if an Security-Group exists;  If missing, the creation of the SG (AWS Security Group) is included in the cloudformation generated by SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Snippet #3 - Conditional Cloudformation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as EC2 from ‘aws-cdk-lib/aws-ec2';
import { EC2Client } from ‘@aws-sdk/client-ec2';
..
..
async function lookupSecurityGroup( securityGroupName: string, 
                                    aws_sdk_credentials: any, )
              : Promise&amp;lt;string | undefined&amp;gt;
{
    const ec2client = new EC2Client( aws_sdk_credentials );
  ..
  .. // see full version of THIS function -&amp;gt; at bottom of this article.
  ..
    const sgresponse = await ec2client.send( sgcommand );
    if ( !sgresponse.SecurityGroups || sgresponse.SecurityGroups!.length &amp;lt; 1) {
        throw .. ..
    } else {
      return new Promise((res) =&amp;gt; res( sgresponse.SecurityGroups[0].GroupId );
    }
} // end of async function.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;// now let’s utilize the above function !!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    await lookupSecurityGroup( SGNAME, aws_sdk_credentials )
    .then((sgid) =&amp;gt; {
        myAppWideSecurityGroup = EC2.SecurityGroup.fromLookupByName( this,  SGNAME, SGNAME, defaultVpc );
        if ( ! isDirtyVPC ) {
           throw Error( `!! SAFETY-CHECK ERROR !! SG ${SGNAME} already exists.  You did NOT provide CLI-arg "--dirtyVPC".\nExiting with error-code ...` );
        }
     }).catch( (e) =&amp;gt; {
        console.log( `Ok. SG Does Not exist. So, creating new SG named ${SGNAME} ...` );
        const mySGProps: EC2.SecurityGroupProps = {
                    vpc: defaultVpc,
                    securityGroupName: SGNAME,
                    description: SGDESC,
                    allowAllOutbound: true,
                    disableInlineRules: true,
        };
        myAppWideSG = new EC2.SecurityGroup( this, SGNAME, mySGProps );
        // commented out: myAppWideSG.addEgressRule( EC2.Peer.anyIpv4(), EC2.Port.allTcp(), "Any Port OUTbound" );

        const cfn_sg = myAppWideSG.node.defaultChild as EC2.CfnSecurityGroup;
        cfn_sg.overrideLogicalId( SGNAME.replace(/-/g, '') ); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last line of code removes all hyphens from SGNAME, so the resulting string can be used as Resource-Name within Cloudformation-template.&lt;/p&gt;

&lt;p&gt;Note: I'm ignoring &lt;code&gt;addEgressRule&lt;/code&gt; since '&lt;code&gt;allowAllOutbound&lt;/code&gt;' is set to &lt;code&gt;true&lt;/code&gt; by default; To add customized rules, set &lt;code&gt;allowAllOutbound=false&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Key Takeways
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vQNixs3P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pblr8c7k24oe1k2b477s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vQNixs3P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pblr8c7k24oe1k2b477s.png" alt="RibbonOnFinger" width="237" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even though this article focused on a very valid common real-world scenario w.r.t. AWS Security Groups, this article offers a simple clean way to combine AWS-SDK with AWS-CDK, to can design/write CDK code that cleanly checks for a pre-existing component (say, a Fargate ECS-Cluster), and skip the generation of Cloudformation accordingly.&lt;/p&gt;

&lt;h1&gt;
  
  
  APPENDIX: full-code
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { EC2Client, DescribeVpcsCommand, DescribeSecurityGroupsCommand } from '@aws-sdk/client-ec2';
import { SecurityGroup } from '@aws-sdk/client-ec2';

export async function lookupSecurityGroup( securityGroupName: string,  aws_sdk_credentials: any, ): Promise&amp;lt;string&amp;gt;
{
  const ec2client = new EC2Client( aws_sdk_credentials );
  const filter = {
      Filters: [ { Name: "isDefault",  Values: ["true"] } ],
      MaxResults: 5, // Number("int");    Attention! Minimum value is 5. Maximum value of 1000.
      DryRun: false,
      // VpcIds: [ "vpc-0123456", ],
  };
  const vpccommand = new DescribeVpcsCommand( filter );
  const vpcresponse = await ec2client.send(vpccommand);
  if ( ! vpcresponse.Vpcs || vpcresponse.Vpcs.length &amp;lt; 1 ) {
      throw new Error('No default VPC found');
  }
  const defaultVpcId = vpcresponse.Vpcs![0].VpcId!;
  // DescribeVpcsResult: https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ec2/interfaces/vpc-8.html
  // {  Vpcs: [{
  //       CidrBlock: "STRING_VALUE",
  //       DhcpOptionsId: "STRING_VALUE",
  //       VpcId: "STRING_VALUE",
  //       IsDefault: true || false,
  //       Tags: [ // TagList
  //       .. ..

  // ---------------------------------- 
  const sgfilter = { // DescribeSecurityGroupsRequest
      Filters: [
          { Name: 'vpc-id', Values: [defaultVpcId], },
          { Name: 'group-name', Values: [securityGroupName], },
               // NOTE: Simpler to use "GroupName" instead !!!
      ],
      DryRun: false,
      MaxResults: 5, // Number("int");    Attention!  Minimum value is 5. Maximum value of 1000.
      // GroupIds: [ "sg-12345678", ],
      // GroupNames: [  "STRING_VALUE", ],
      // NextToken: "STRING_VALUE",
  };
  const sgcommand = new DescribeSecurityGroupsCommand( sgfilter );
  const sgresponse = await ec2client.send(sgcommand);
  // { // DescribeSecurityGroupsResult
  //   SecurityGroups: [ // SecurityGroupList
  //     { // SecurityGroup
  //       Description: "STRING_VALUE",
  //       GroupName: "STRING_VALUE",
  if ( ! sgresponse.SecurityGroups || sgresponse.SecurityGroups!.length &amp;lt; 1 ) {
      throw new Error(`No SG found with name ${securityGroupName}`);
  }

  return sgresponse.SecurityGroups![0].GroupId!;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>devops</category>
      <category>cdk</category>
      <category>awssdk</category>
      <category>enterprise</category>
    </item>
  </channel>
</rss>
