<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Cerulean Cloud Blog]]></title><description><![CDATA[Cerulean Cloud Blog aims to share cloud engineering concepts that work for people of all levels, from beginners to advanced engineers.]]></description><link>https://blog.ceruleancloud.ca</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1742600784271/24d7d8e0-6575-4404-8455-10f78cae1ebc.png</url><title>Cerulean Cloud Blog</title><link>https://blog.ceruleancloud.ca</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 15:13:05 GMT</lastBuildDate><atom:link href="https://blog.ceruleancloud.ca/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Optimizing AWS Costs: NAT Gateways, S3 Storage Classes, and EBS Lifecycle Management]]></title><description><![CDATA[Most AWS environments carry somewhere between 20% and 35% in avoidable spend. This is not a controversial claim. Industry reports from Flexera and Gartner have consistently placed cloud waste in that ]]></description><link>https://blog.ceruleancloud.ca/optimizing-aws-costs-nat-gateways-s3-storage-classes-and-ebs-lifecycle-management</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/optimizing-aws-costs-nat-gateways-s3-storage-classes-and-ebs-lifecycle-management</guid><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Tue, 31 Mar 2026 14:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6722707bc8ef419b2aa32d93/375f6a56-44b3-4ab6-b524-1ee77920522e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most AWS environments carry somewhere between 20% and 35% in avoidable spend. This is not a controversial claim. Industry reports from Flexera and Gartner have consistently placed cloud waste in that range, and the pattern holds regardless of whether the organization is a startup running a handful of services or an enterprise managing hundreds of accounts. The waste rarely comes from one large, obvious mistake. It accumulates through a series of small architectural decisions that made sense at the time but were never revisited as workloads evolved. What makes AWS cost waste particularly difficult to catch is that the billing model is designed around granularity. You are charged per hour, per gigabyte, per request, and per data transfer path. Each individual charge looks reasonable in isolation. It is only when you trace the full path of a request through your infrastructure, accounting for every service it touches along the way, that the compounding effect becomes visible. This post examines three of the most common cost leaks we see in AWS environments. Rather than listing surface-level tips, we will walk through the mechanics of each one: why it happens, how to identify it in your own account, and how to address it with specific AWS tools and configuration changes.</p>
<h2>NAT Gateway data processing charges</h2>
<p>NAT Gateways are one of the most commonly misunderstood cost centers in AWS networking. When you provision a NAT Gateway, AWS charges you in two dimensions simultaneously: a flat hourly rate of \(0.045 per hour (in us-east-1), and a per-gigabyte data processing charge of \)0.045 for every gigabyte that flows through the gateway in either direction. These charges apply on top of any standard data transfer fees that AWS levies for cross-AZ or internet-bound traffic. The hourly charge alone works out to roughly \(32.40 per month for a single NAT Gateway running continuously. Since AWS recommends deploying one NAT Gateway per Availability Zone for high availability, a standard three-AZ production architecture carries a baseline cost of approximately \)97 per month before a single byte of data is processed. This is the cost of the NAT Gateways simply existing. The data processing charge is where the bill compounds.</p>
<p>Consider a common scenario: your application runs in private subnets and makes regular API calls to AWS services like S3, DynamoDB, SQS, or CloudWatch. By default, all of this traffic routes through the NAT Gateway, and every gigabyte is charged at \(0.045. A workload pulling 500 GB of data from S3 per month through a NAT Gateway incurs \)22.50 in processing charges alone, for traffic that could flow entirely for free through a properly configured VPC endpoint. The compounding gets worse when you factor in container workloads. ECS and EKS tasks running in private subnets pull container images from Amazon ECR through the NAT Gateway. A 500 MB container image pulled 100 times per month represents 50 GB of NAT Gateway traffic, adding $2.25 per month per image. Across a fleet of microservices with frequent deployments, this accumulates into a meaningful line item.</p>
<p><strong>How to identify it:</strong> Open AWS Cost Explorer and filter by service for "VPC" or "EC2-Other." Look for the line item labeled "NatGateway-Bytes" under usage type. If you are processing more than a few gigabytes per month, you are likely paying for traffic that could be routed more efficiently. You can also enable VPC Flow Logs and analyze them to understand which services and endpoints are generating the most NAT Gateway traffic.</p>
<p><strong>How to fix it:</strong> The most impactful change is deploying VPC Gateway Endpoints for S3 and DynamoDB. Gateway Endpoints are completely free. There is no hourly charge and no data processing charge. Traffic routes over AWS's private network backbone instead of traversing the NAT Gateway. The setup takes minutes: you create the endpoint in your VPC, associate it with the relevant route tables, and the traffic is redirected automatically. No application code changes are required.</p>
<p>For other AWS services like SQS, SNS, CloudWatch, ECR, and Secrets Manager, you can deploy VPC Interface Endpoints (powered by AWS PrivateLink). These do carry a cost of \(0.01 per hour plus \)0.01 per GB of data processed, but this is still significantly cheaper than the $0.045 per GB you would pay through the NAT Gateway. The cost difference becomes substantial at any meaningful traffic volume. For ECR specifically, deploying an Interface Endpoint also eliminates the NAT Gateway charges incurred during container image pulls, which can represent a surprisingly large portion of total NAT traffic in containerized environments. It is worth noting that deploying these endpoints is not an all-or-nothing decision. You can start by deploying the free S3 and DynamoDB Gateway Endpoints, monitor the impact on your NAT Gateway data processing charges for a billing cycle, and then evaluate whether Interface Endpoints for other services are justified based on your traffic patterns.</p>
<h2>S3 storage class mismatches</h2>
<p>Amazon S3 pricing is structured around storage classes, each designed for a different access pattern. S3 Standard, the default class, costs \(0.023 per GB per month in us-east-1. This is appropriate for data that is accessed frequently and requires low-latency retrieval. The problem is that most teams store everything in S3 Standard regardless of how often the data is actually accessed, and they rarely revisit this decision as data accumulates over time. The cost difference between storage classes is significant. S3 Standard-Infrequent Access (Standard-IA) costs \)0.0125 per GB per month, roughly half the price of Standard. S3 Glacier Instant Retrieval, which still provides millisecond-level access latency, costs \(0.004 per GB per month, which is about 83% less than Standard. S3 Glacier Deep Archive, designed for compliance and regulatory data that is accessed very rarely, costs just \)0.00099 per GB per month.</p>
<p><strong>To put these numbers in practical terms:</strong> an organization storing 10 TB of data entirely in S3 Standard pays approximately \(235 per month in storage costs. If 70% of that data is archival (log files, old backups, historical exports, compliance records) and could be moved to Glacier Instant Retrieval, the storage cost for that 7 TB drops from \)164 to \(28 per month. That is a saving of \)136 per month, or over $1,600 per year, for a single storage optimization on a relatively modest data footprint. As data volumes grow into the tens or hundreds of terabytes, these savings scale proportionally. The reason this waste persists is partly behavioral. Teams create S3 buckets, upload data, and move on. There is no built-in mechanism that alerts you when the majority of objects in a bucket have not been accessed in months. The data just sits there, billed at the Standard rate, growing quietly with every new upload.</p>
<p><strong>How to identify it:</strong> AWS provides two tools that make this analysis straightforward. The first is S3 Storage Lens, which gives you an account-wide or organization-wide view of your S3 usage broken down by storage class, bucket, region, and access patterns. It will show you exactly how much data sits in each storage class and highlight buckets where a large percentage of objects have not been accessed recently. The second tool is S3 Storage Class Analysis, which you can enable on individual buckets. It monitors object-level access patterns over a 30-day period and generates recommendations for which objects would benefit from transitioning to a lower-cost storage class.</p>
<p><strong>How to fix it:</strong> The simplest and most broadly applicable fix is enabling S3 Intelligent-Tiering on buckets where access patterns are unpredictable or mixed. Intelligent-Tiering automatically monitors each object's access frequency and moves it between a frequent access tier and an infrequent access tier after 30 days of no access. If the object is not accessed for 90 days, it can optionally be moved to an archive access tier, and after 180 days, to a deep archive tier.</p>
<p>The key advantage of Intelligent-Tiering is that there are no retrieval fees when objects move back to the frequent access tier, so you do not pay a penalty if an archived object is suddenly needed. The trade-off is a small monthly monitoring fee of $0.0025 per 1,000 objects, which is negligible for most workloads but can add up if you have millions of very small objects (under 128 KB each).</p>
<p>For data that you know is archival from the start, such as log files or compliance backups, the more direct approach is configuring S3 Lifecycle Rules on the relevant buckets. A lifecycle rule can automatically transition objects to Standard-IA after 30 days, to Glacier Instant Retrieval after 90 days, and to Glacier Deep Archive after 365 days. You can also configure lifecycle rules to expire (delete) objects after a defined retention period, which prevents old data from accumulating indefinitely.</p>
<p>One important caveat: both Standard-IA and One Zone-IA enforce a minimum storage duration of 30 days. If you delete or overwrite an object before the 30-day mark, you are still charged for the full 30 days at the IA storage rate. This means lifecycle transitions should be configured with retention requirements in mind, not applied indiscriminately to all buckets.</p>
<h2>Unattached EBS volumes and orphaned snapshots</h2>
<p>This is perhaps the most straightforward cost leak on this list, and also one of the most persistent. When you terminate an EC2 instance, the attached EBS volumes are not always deleted along with it. Whether the volume is deleted depends on the "Delete on Termination" attribute, which is set at the time the volume is attached. For root volumes, this attribute defaults to true, but for additional data volumes, it often defaults to false. This means that terminating an EC2 instance can leave behind one or more "orphaned" EBS volumes that are no longer attached to any running instance but continue to incur storage charges.</p>
<p>The cost of a single orphaned volume depends on its type and size. A 100 GB gp3 volume costs approximately $8 per month. That does not sound like much in isolation, but in environments where instances are created and terminated frequently, such as development and testing environments, CI/CD build fleets, or auto-scaling groups, orphaned volumes accumulate over time. It is not uncommon to find dozens of unattached volumes in an account that has been active for a year or more.</p>
<p>EBS snapshots follow a similar pattern but can be even harder to catch. Snapshots are incremental backups of EBS volumes, and teams often configure automated snapshot schedules using Amazon Data Lifecycle Manager (DLM) or custom Lambda functions as a backup strategy. This is good practice for data protection. The problem arises when snapshot retention policies are not configured, or when they are configured with overly generous retention periods. Without a retention policy, every snapshot that is created is retained indefinitely, and each one incurs storage charges based on the amount of changed data it contains. Over months and years, the cumulative cost of forgotten snapshots can exceed the cost of the volumes they were meant to protect.</p>
<p><strong>How to identify it:</strong> In the EC2 console, navigate to the Volumes section and filter by the "Available" state. Every volume listed as "Available" is not attached to any instance and is almost certainly unnecessary. For snapshots, sort by creation date and cross-reference against your retention requirements. Any snapshot older than your retention policy dictates is a candidate for deletion. AWS Trusted Advisor also includes a check for underutilized EBS volumes and can surface these findings automatically. You can also run a quick audit from the AWS CLI.</p>
<p>The following command lists all unattached EBS volumes in your current region:</p>
<p><code>aws ec2 describe-volumes   --filters Name=status,Values=available   --query "Volumes[].{ID:VolumeId,Size:Size,Type:VolumeType,Created:CreateTime}"   --output table</code></p>
<p>For snapshots, a similar approach works. You can list all snapshots owned by your account and review their age and associated volume:</p>
<p><code>aws ec2 describe-snapshots   --owner-ids self   --query "Snapshots[].{ID:SnapshotId,VolumeId:VolumeId,Size:VolumeSize,Started:StartTime}"   --output table</code></p>
<p><strong>How to fix it:</strong> For orphaned volumes, the fix is simply deletion after verifying that no critical data resides on them. If you are uncertain, you can create a final snapshot of the volume before deleting it, and then set a lifecycle policy on that snapshot to expire it after a defined period. For snapshots, the recommended approach is configuring Amazon Data Lifecycle Manager with explicit retention rules.</p>
<p>DLM allows you to define automated snapshot policies that specify how many snapshots to retain (for example, keep the last 7 daily snapshots and the last 4 weekly snapshots) and automatically deletes older snapshots when the retention limit is reached. This eliminates the manual overhead of snapshot cleanup and ensures that backup storage costs remain predictable over time. Going forward, it is also worth reviewing the "Delete on Termination" attribute for EBS volumes in your launch templates and AMIs.</p>
<p>Setting this attribute to true for non-persistent data volumes ensures that future instance terminations do not leave behind orphaned storage. For volumes that contain data you need to retain, a better pattern is snapshotting the volume before termination (using a lifecycle hook in an Auto Scaling Group, for example) and then letting DLM manage the snapshot retention.</p>
<h2>Building cost awareness into your operational rhythm</h2>
<p>The three cost leaks described above share a common characteristic: none of them are the result of a single bad decision. They are the product of reasonable initial configurations that were never revisited as the environment grew and workloads changed. NAT Gateways are deployed because private subnets need internet access.</p>
<p>S3 data is stored in Standard because it is the default. EBS volumes are left behind because the termination behavior was not explicitly configured. The underlying lesson is that AWS cost optimization is not a one-time audit. It is an ongoing discipline that needs to be embedded into your operational practices.</p>
<p>A monthly review of Cost Explorer anomalies, a quarterly check of S3 storage distribution across classes, and a periodic sweep for orphaned resources will catch most waste before it compounds.</p>
<p>AWS Budgets, which is free to use, can be configured to send alerts when spending in a particular service or account exceeds a threshold, giving you early visibility into unexpected cost increases. The tools exist. The pricing data is transparent. What most teams lack is not information but the habit of looking.</p>
]]></content:encoded></item><item><title><![CDATA[When to Use ECS vs EKS vs Lambda: A Decision Framework]]></title><description><![CDATA[You're building on AWS.
You know your workload needs compute. And now you're staring at three options. ECS, EKS, and Lambda. Each with its own ecosystem of blog posts telling you it's the right choice]]></description><link>https://blog.ceruleancloud.ca/when-to-use-ecs-vs-eks-vs-lambda-a-decision-framework</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/when-to-use-ecs-vs-eks-vs-lambda-a-decision-framework</guid><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Sun, 29 Mar 2026 15:27:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6722707bc8ef419b2aa32d93/f67b0249-3bd3-4a67-85cb-43f3dd68182a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You're building on AWS.</p>
<p>You know your workload needs compute. And now you're staring at three options. ECS, EKS, and Lambda. Each with its own ecosystem of blog posts telling you it's the right choice.</p>
<p>Here's the truth: there's no universally correct answer. But there <em>is</em> a structured way to decide. At Cerulean Cloud, we walk folks through this decision regularly, and we've found that most teams overcomplicate it. The right choice usually comes down to four factors.</p>
<h2>The Four Factors That Actually Matter</h2>
<p>Before comparing services feature-by-feature, zoom out. Every compute decision on AWS comes down to these questions:</p>
<p><strong>1. Operational ownership</strong>: How much infrastructure management does your team want to own?</p>
<p><strong>2. Workload profile</strong>: Is your workload long-running, event-driven, or somewhere in between?</p>
<p><strong>3. Team expertise</strong>: Does your team already know Kubernetes? Docker? Neither?</p>
<p><strong>4. Growth trajectory</strong>: Where will this workload be in 18 months?</p>
<p>Let's run each service through these lenses.</p>
<h2>Lambda: When You Want AWS to Handle Everything</h2>
<p>Lambda is the right starting point for workloads that are <strong>event-driven, short-lived, and unpredictable in volume</strong>. Think API backends that spike during business hours and flatline at night. Think file processing triggers, webhook handlers, or scheduled ETL jobs that run for a few minutes and disappear.</p>
<p><strong>Choose Lambda when:</strong></p>
<ul>
<li><p>Your functions complete in under 15 minutes (hard limit).</p>
</li>
<li><p>Traffic is bursty or unpredictable and you don't want to pay for idle capacity.</p>
</li>
<li><p>Your team is small and you'd rather spend engineering cycles on product, not infrastructure.</p>
</li>
<li><p>You're building event-driven architectures with SQS, SNS, EventBridge, or S3 triggers.</p>
</li>
</ul>
<p><strong>Think twice about Lambda when:</strong></p>
<ul>
<li><p>You need persistent connections (WebSockets, long-polling) at scale.</p>
</li>
<li><p>Cold starts are a dealbreaker for your latency requirements - although there is Lambda warm configurations to tackle this.</p>
</li>
<li><p>Your application has complex dependency trees or large container images</p>
</li>
<li><p>You're running compute-heavy workloads that consistently need 10+ minutes per invocation.</p>
</li>
</ul>
<p>Lambda's superpower is that there's genuinely nothing to manage. No clusters, no capacity planning, no patching. But that simplicity comes with constraints. The moment your workload starts pushing against those constraints, you're fighting the platform instead of building on it.</p>
<h2>ECS: When You Want Containers Without the Kubernetes Tax</h2>
<p>ECS is AWS's own container orchestration service, and it's quietly become the best default choice for teams that need containers but don't need Kubernetes.</p>
<p>Pair ECS with <strong>Fargate</strong> and you get serverless containers. No EC2 instances to manage, no cluster capacity to worry about. Pair it with <strong>EC2 launch type</strong> and you get full control over the underlying hosts when you need it (GPU workloads, specific instance types, cost optimization through Reserved Instances).</p>
<p><strong>Choose ECS when:</strong></p>
<ul>
<li><p>Your workload is long-running and needs to be containerized (APIs, background workers, microservices).</p>
</li>
<li><p>Your team is comfortable with Docker but doesn't have Kubernetes expertise.</p>
</li>
<li><p>You want tight, native integration with the AWS ecosystem like ALB, CloudWatch, IAM, Service Connect without glue code.</p>
</li>
<li><p>You value simplicity and fast time-to-production over ecosystem portability.</p>
</li>
</ul>
<p><strong>Think twice about ECS when:</strong></p>
<ul>
<li><p>Multi-cloud or hybrid-cloud portability is a hard requirement today</p>
</li>
<li><p>You need advanced scheduling, custom controllers, or operators that only exist in the Kubernetes ecosystem.</p>
</li>
<li><p>Your team already runs Kubernetes elsewhere and wants a consistent operational model across environments.</p>
</li>
</ul>
<p>Here's what we tell folks candidly: ECS is underrated. It does 80% of what Kubernetes does with 30% of the operational complexity. For most mid-market teams running purely on AWS, ECS with Fargate is the fastest path to production-grade container workloads.</p>
<h2>EKS: When You Genuinely Need Kubernetes</h2>
<p>EKS is managed Kubernetes on AWS. It gives you the full Kubernetes API, the massive open-source ecosystem, and the ability to run the same workload definition on any cloud or on-premises cluster.</p>
<p>But Kubernetes is not free. Not in cost, and certainly not in operational complexity. EKS is the right choice when the power of the Kubernetes ecosystem solves a problem that ECS cannot.</p>
<p><strong>Choose EKS when:</strong></p>
<ul>
<li><p>You have a team that already knows Kubernetes and operates it confidently.</p>
</li>
<li><p>Portability is a real, current requirement and you're running workloads across AWS, GCP, Azure, or on-prem.</p>
</li>
<li><p>You need the Kubernetes ecosystem: Istio for service mesh, Argo for GitOps, custom operators for stateful workloads, Karpenter for intelligent autoscaling.</p>
</li>
<li><p>You're running a platform team that serves multiple internal development teams and needs namespace-level isolation, RBAC, and self-service deployment.</p>
</li>
</ul>
<p><strong>Think twice about EKS when:</strong></p>
<ul>
<li><p>"We might go multi-cloud someday" is the only reason Kubernetes is on the table. Hypothetical portability is expensive portability.</p>
</li>
<li><p>Your team doesn't have Kubernetes experience and you'd be learning it in production.</p>
</li>
<li><p>You're a team of 3–10 engineers and you'd be spending 20–30% of your time on cluster operations instead of product development.</p>
</li>
<li><p>Your workload is simple enough that ECS or Lambda would handle it with less overhead.</p>
</li>
</ul>
<p>We've seen this pattern repeatedly: a startup adopts EKS because it feels like the "serious" choice, then spends six months building platform tooling before shipping a single feature. Kubernetes is powerful. But power you don't need is just overhead.</p>
<h2>The Decision Flow</h2>
<p>Here's the simplified framework we use at Cerulean Cloud during architecture engagements:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6722707bc8ef419b2aa32d93/d438da45-c6fa-4561-8613-db5a7c97d89e.png" alt="" style="display:block;margin:0 auto" />

<p><strong>Start with Lambda.</strong> If your workload is event-driven, runs under 15 minutes, and doesn't need persistent compute then Lambda is your answer. Stop here.</p>
<p><strong>If Lambda doesn't fit, default to ECS on Fargate.</strong> Long-running containers, microservices, background workers. ECS handles all of it with minimal operational burden. Use EC2 launch type only if you need GPU, specific instance families, or want to optimize cost with Reserved Instances.</p>
<p><strong>Graduate to EKS only when you have a Kubernetes-specific reason.</strong> Portability requirements, ecosystem tooling (service mesh, GitOps, custom operators), or a platform engineering team serving multiple tenants. If you can't name the specific Kubernetes capability you need, you probably don't need EKS.</p>
<h2>The Hybrid Reality</h2>
<p>In practice, most mature AWS environments use more than one of these services. Lambda handles event-driven aspects like processing S3 uploads, running scheduled tasks, powering lightweight APIs. ECS runs the core application services. EKS exists when there's a genuine platform engineering need.</p>
<p>The mistake is treating this as a one-size-fits-all decision. It's not. It's a per-workload decision, and the best architectures are intentional about which workloads land where.</p>
<h2>What This Looks Like in Practice</h2>
<p>When we run architecture engagements at Cerulean Cloud, the compute decision is never made in isolation. It connects directly to networking (VPC design, service discovery), observability (CloudWatch vs. third-party), CI/CD (how you deploy shapes what you deploy to), and cost (Fargate vs. EC2, Lambda pricing at scale).</p>
]]></content:encoded></item><item><title><![CDATA[Why I built stressllm?]]></title><description><![CDATA[A few weeks ago I wrote about building a multi agent system on my old personal laptop. You can read more about it here. This project failed miserably because of my hardware limitations. My hardware wa]]></description><link>https://blog.ceruleancloud.ca/why-i-built-stressllm</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/why-i-built-stressllm</guid><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Sat, 07 Mar 2026 02:03:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6722707bc8ef419b2aa32d93/570c56ee-bddb-454f-bdb3-1f8850b117bd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A few weeks ago I wrote about building a multi agent system on my old personal laptop. You can read more about it <a href="https://blog.ceruleancloud.ca/building-a-multi-agent-system-to-track-real-madrid-matches-using-aws-strands-and-ollama">here</a>. This project failed miserably because of my hardware limitations. My hardware was theoretically sufficient, but the system was unusable in practice. I realized I was flying blind against the <strong>KV Cache</strong>; the "working memory" that scales with context. I needed a way to know exactly where my hardware would "choke" before I spent hours debugging a lagging agent.</p>
<p>So, I spent the weekend <strong>vibe-coding</strong> <a href="https://pypi.org/project/stressllm/"><strong>stressllm</strong></a>: a Python CLI tool that performs context saturation tests to find your hardware's breaking point.</p>
<h2>The Core Bottleneck: VRAM vs. Context</h2>
<p>Most people only look at "Static VRAM" i.e the memory needed to load the model weights. But the real killer is <strong>Dynamic VRAM</strong>.</p>
<p>As your conversation grows, the model needs to store the "Keys" and "Values" (KV Cache) of every token to avoid re-processing the entire prompt. This grows linearly. On my 6-year-old laptop with 2GB of VRAM, the moment that cache spills over into system RAM, performance falls off a cliff.</p>
<h2>Under the Hood: The Architecture</h2>
<p>I built StressLLM with a clean separation of concerns: <strong>The Watcher</strong> and <strong>The Worker</strong>.</p>
<p>I split the tool into two main components: <code>probe.py</code> (the observer) and <code>engine.py</code> (the executor).</p>
<h3><code>probe.py</code>: Hardware Telemetry &amp; Constraints</h3>
<p>This module is responsible for heartbeat-style monitoring of your system.</p>
<ul>
<li><p><strong>NVIDIA Only:</strong> The tool currently relies on <code>pynvml</code> (NVIDIA Management Library). It specifically looks for <code>nvml.dll</code> on Windows or the standard drivers on Linux.</p>
</li>
<li><p><strong>Graceful Fallback:</strong> When the GPU isn't available, the tool automatically pivots to reporting system-wide <strong>RAM</strong> and <strong>CPU</strong> usage via <code>psutil</code>.</p>
</li>
<li><p><strong>Error Isolation:</strong> Each sensor (VRAM, Temperature, CPU) is independent. If a single telemetry call fails, the rest of the stats are still yielded.</p>
</li>
</ul>
<h3>2. <code>engine.py</code>: The Saturation Logic</h3>
<p>This is the "stress" part of the tester. It uses a specific strategy to find the breaking point:</p>
<ul>
<li><p><strong>Synthetic Pressure:</strong> Instead of asking the model to "think," it uses a <code>wordpool.txt</code> to generate a random prompt of a specific token length. The instruction is simple: <em>"Read this and say 'done'."</em> This isolates <strong>Prompt Evaluation</strong> speed and <strong>KV Cache</strong> overhead from actual generation.</p>
</li>
<li><p><strong>The Generator Pattern:</strong> The test is written as a Python generator (<code>yield</code>). This is a safety feature. If the model causes a total system hang or an Out-of-Memory (OOM) error at 128k context, the tool has already "yielded" the successful results for 2k, 8k, and 32k.</p>
</li>
<li><p><strong>Direct GGUF vs. Ollama:</strong> It supports two paths. It can call the <strong>Ollama API</strong> (testing your local server) or load a <strong>.gguf</strong> file directly via <code>llama-cpp-python</code>, which allows for manual control over <code>n_gpu_layers</code>.</p>
</li>
<li><p><strong>The Verdict:</strong> The code maps Tokens-Per-Second (TPS) to a status. Anything under 5 TPS is flagged as the "Cliff" meaning the KV cache has likely spilled into your slower system RAM.</p>
</li>
</ul>
<h3>The Verdict: Mapping the Performance Cliff</h3>
<p>The tool doesn't just give you numbers; it gives you a "vibe check" based on the <code>_verdict</code> logic:</p>
<ul>
<li><p><strong>✅ Smooth (&gt;15 TPS):</strong> Native GPU speeds. Your agents will feel snappy.</p>
</li>
<li><p><strong>⚠️ Slowing (5-15 TPS):</strong> You’ve likely hit the "Knee" where the KV cache is spilling into system RAM.</p>
</li>
<li><p><strong>💀 Cliff (&lt;5 TPS):</strong> Total saturation. Time to lower your <code>num_ctx</code> or buy a better GPU.</p>
</li>
</ul>
<h2>Getting Started</h2>
<p>I’ve pushed the tool to PyPI and GitHub. You can test your own local setup in seconds:</p>
<p>You can use the following commands to test the tool</p>
<pre><code class="language-plaintext">#install via pip
pip install stressllm

#list available models 
stressllm models

#run test
stressllm run &lt;modelname&gt; --depth 2
</code></pre>
<p>Whether you are building agentic frameworks or just running local LLMs for privacy, you need to know your limits. Stop guessing and start stressing.</p>
<p><strong>Find it on PyPI:</strong> <a href="https://pypi.org/project/stressllm/">pypi.org/project/stressllm/</a></p>
]]></content:encoded></item><item><title><![CDATA[Building a Multi Agent System to Track Real Madrid Matches Using AWS Strands and Ollama]]></title><description><![CDATA[I like soccer and Real Madrid, but I’m not the diehard kind who can recite every fixture by heart. So naturally, I end up missing mid-week games and that usually end up to be the best matches. I wante]]></description><link>https://blog.ceruleancloud.ca/building-a-multi-agent-system-to-track-real-madrid-matches-using-aws-strands-and-ollama</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/building-a-multi-agent-system-to-track-real-madrid-matches-using-aws-strands-and-ollama</guid><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Tue, 24 Feb 2026 01:10:31 GMT</pubDate><enclosure url="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/6722707bc8ef419b2aa32d93/e914966c-dc30-4d4e-baf0-90059f4ea064.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I like soccer and Real Madrid, but I’m not the diehard kind who can recite every fixture by heart. So naturally, I end up missing mid-week games and that usually end up to be the best matches. I wanted to solve for this…</p>
<p>Then at least I know which match is worth following, so even if I cannot watch it, I can still track the score online.</p>
<p>And I had three options…</p>
<p>🔺Google the fixtures manually (Zero points for style).</p>
<p>🔺Write a cron job to ping an API and send a dry text (Functional, but boring).</p>
<p>🔺Build a multi-agent system that finds the game, decides if the "hype factor" is worth it, and notifies me with a reason to watch.</p>
<p>Since we’re well past 2023, I chose the obvious one. 🤷🏽‍♂️</p>
<p>I spent the weekend building a local agentic system using AWS Strands and Ollama.</p>
<p>This project uses:</p>
<ul>
<li><p>AWS Strands for agent orchestration</p>
</li>
<li><p>Ollama for running language models locally</p>
</li>
<li><p>A Football API for match data</p>
</li>
<li><p>Telegram API for notifications</p>
</li>
</ul>
<h2>What Is AWS Strands</h2>
<p>AWS Strands is a lightweight agent orchestration framework that allows you to define agents, connect them to language models, and expose structured tools that those agents can call. Instead of writing manual glue code between LLM calls and functions, Strands lets you describe:</p>
<ul>
<li><p>The agent’s role</p>
</li>
<li><p>The model it uses</p>
</li>
<li><p>The tools it can call</p>
</li>
<li><p>The rules it must follow</p>
</li>
</ul>
<p>Strands handles the tool calling loop internally. When the model decides to call a tool, Strands executes the function, captures the output, and feeds it back into the model until the task completes.</p>
<p>In this project, Strands acts as the controller. It ensures the supervisor calls the analyzer first and the communication step second. It enforces order without requiring complex orchestration code.</p>
<h2>What Is Ollama and Why Use It</h2>
<p>Ollama is a local runtime that allows you to run open source language models on your own machine. It exposes a simple HTTP server, typically at:<a href="http://localhost:11434">http://localhost:11434</a></p>
<p>Instead of sending prompts to a cloud provider, your application sends them to Ollama, which runs the model locally.</p>
<p>This gives you:</p>
<ul>
<li><p>Full local control</p>
</li>
<li><p>No external inference costs</p>
</li>
<li><p>Faster iteration during development</p>
</li>
<li><p>No dependency on external model APIs</p>
</li>
</ul>
<p>In this project, Ollama runs small models such as Gemma 2B to handle structured reasoning.</p>
<h2>Installing Ollama on Windows</h2>
<h3>Step 1: Download</h3>
<p>Go to the official website: <a href="https://ollama.com">https://ollama.com</a></p>
<p>Download the Windows installer.</p>
<h3>Step 2: Install</h3>
<p>Run the installer and follow the default setup. Ollama installs as a background service.</p>
<h3>Step 3: Verify</h3>
<p>Open PowerShell and run:</p>
<pre><code class="language-plaintext">ollama --version
</code></pre>
<p>If installed correctly, it prints the version.</p>
<h2>Pulling and Running a Model</h2>
<p>To download a model such as Gemma 2B:</p>
<pre><code class="language-plaintext">ollama pull gemma:2b
</code></pre>
<p>To see all installed models:</p>
<pre><code class="language-plaintext">ollama list
</code></pre>
<p>To run the model interactively:</p>
<pre><code class="language-plaintext">ollama run gemma:2b
</code></pre>
<p>To call the model from your application, send an HTTP request to:</p>
<pre><code class="language-plaintext">http://localhost:11434/api/generate
</code></pre>
<p>Example:</p>
<pre><code class="language-plaintext">curl http://localhost:11434/api/generate -d '{
  "model": "gemma:2b",
  "prompt": "What can you do?"
}'
</code></pre>
<p>Your Strands agent internally calls this endpoint when configured with <code>OllamaModel</code>.</p>
<h2>High Level Design - Multi agent workflow</h2>
<p>The system follows a simple multi step flow:</p>
<ol>
<li><p>A supervisor agent receives a task.</p>
</li>
<li><p>An analyzer component fetches match data from a Football API.</p>
</li>
<li><p>The analyzer evaluates whether the match is worth watching.</p>
</li>
<li><p>If the match crosses a defined threshold, a communication component sends a message using the Telegram API.</p>
</li>
</ol>
<p>There are no complex distributed services. Everything runs locally except the external APIs.</p>
<p>The intelligence sits in the analysis step. The supervisor enforces order. The communication layer focuses only on delivery.</p>
<h2>Understanding the Sequence Diagram</h2>
<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/6722707bc8ef419b2aa32d93/07305b8b-307c-4e11-91ec-6328dbc607a6.jpg" alt="" style="display:block;margin:0 auto" />

<p>The sequence diagram above shows key components of the system and how the query flows. The flow is linear and disciplined.</p>
<h3>Step 1: Supervisor Initiates the Task</h3>
<p>The Supervisor starts the process by asking the Analyzer to check whether there is a Real Madrid match today. The Supervisor does not fetch data itself. It delegates.</p>
<p>This separation ensures orchestration logic stays clean.</p>
<h3>Step 2: Analyzer Calls the Football API</h3>
<p>The Analyzer sends a request to the Football API to retrieve match data. This includes opponent, competition, timing, and other relevant details.</p>
<p>The Football API responds with structured match data.</p>
<h3>Step 3: Analyzer Performs Reasoning</h3>
<p>Once the Analyzer receives match data, it evaluates whether there is a compelling reason to watch the match. This step uses a language model running locally through Ollama.</p>
<p>The reasoning can include:</p>
<ul>
<li><p>Competition type</p>
</li>
<li><p>Rivalry level</p>
</li>
<li><p>Importance of the fixture</p>
</li>
<li><p>Context around standings</p>
</li>
</ul>
<p>If the match meets the criteria, the Analyzer returns a formatted result to the Supervisor.</p>
<p>If not, it simply returns nothing significant.</p>
<h3>Step 4: Supervisor Triggers Notification</h3>
<p>If the Analyzer returns a positive result, the Supervis calls the Comms component.</p>
<h3>Step 5: Comms Calls Telegram API</h3>
<p>The Comms component sends the final message to the Telegram API, which posts the notification.</p>
<h2>Why the System Struggled on My Laptop</h2>
<p>The architecture was simple but the limitation was hardware.</p>
<p>My laptop runs on an older Intel i5 processor with only 2GB of VRAM. Even small models require consistent memory allocation. When GPU memory is insufficient, the system offloads computation to system RAM, which increases latency and reduces stability.</p>
<p>What I observed:</p>
<ul>
<li><p>Slow inference times</p>
</li>
<li><p>Occasional freezing</p>
</li>
<li><p>Inconsistent reasoning output</p>
</li>
<li><p>Reduced reliability when chaining multiple steps</p>
</li>
</ul>
<p>Multi step workflows amplify instability because each step depends on the previous output. When inference becomes slow or inconsistent, the whole pipeline suffers.</p>
<p>Building locally forced me to understand how models are loaded, served, and executed. That insight is difficult to gain when everything runs behind a managed API.</p>
]]></content:encoded></item><item><title><![CDATA[Three AWS MCP Servers you should use today]]></title><description><![CDATA[In the rapidly evolving landscape of Generative AI, giving your LLM (Large Language Model) access to real-time data and specialized tools is the difference between a generic chatbot and a powerful AI assistant.
AWS has embraced the Model Context Prot...]]></description><link>https://blog.ceruleancloud.ca/three-aws-mcp-servers-you-should-use-today</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/three-aws-mcp-servers-you-should-use-today</guid><category><![CDATA[AI]]></category><category><![CDATA[mcp]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Sat, 27 Dec 2025 19:35:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766864035988/4df3094a-8354-475e-ba85-9e59822e036d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the rapidly evolving landscape of Generative AI, giving your LLM (Large Language Model) access to real-time data and specialized tools is the difference between a generic chatbot and a powerful AI assistant.</p>
<p>AWS has embraced the <strong>Model Context Protocol (MCP)</strong>, providing a suite of servers that allow AI assistants to interact directly with AWS infrastructure, documentation, and architecture tools. Learn more about AWS MCP servers <a target="_blank" href="https://awslabs.github.io/mcp/">here</a>.</p>
<h2 id="heading-what-is-mcp">What is MCP?</h2>
<p>The <strong>Model Context Protocol (MCP)</strong> is an open-source standard, originally developed by Anthropic, that acts as a "universal translator" between AI models and external data sources.</p>
<p>Historically, connecting an AI to a specific tool required custom code, unique API integrations, and complex data parsing. MCP solves this by providing a standardized client-server architecture:</p>
<ul>
<li><p><strong>MCP Client:</strong> Your AI interface (e.g., Kiro, Claude Desktop or Amazon Q Developer).</p>
</li>
<li><p><strong>MCP Server:</strong> A lightweight program that exposes specific tools or data (e.g., AWS Docs, GitHub, or a Database).</p>
</li>
</ul>
<p>By using MCP, you can "plug in" expert capabilities to your AI assistant without writing any integration glue-code.</p>
<h2 id="heading-connecting-to-aws-mcp-servers">Connecting to AWS MCP Servers</h2>
<p>Most AWS MCP servers are hosted on <strong>GitHub (awslabs/mcp)</strong> and can be run locally or via remote managed endpoints. To connect them, you generally follow these steps:</p>
<ol>
<li><p><strong>Install a Manager:</strong> Most servers require <strong>Python 3.10+</strong> and the <strong>uv</strong> package manager.</p>
</li>
<li><p><strong>Configure Credentials:</strong> Ensure your local environment has active AWS credentials (via <code>aws configure</code> or environment variables) with the necessary IAM permissions. <em>You only need this to work with your AWS environment.</em></p>
</li>
<li><p><strong>Update Client Config:</strong> Add the server details to your MCP client’s configuration file (usually <code>mcp.json</code>).</p>
</li>
</ol>
<h3 id="heading-example-configuration-snippet">Example Configuration Snippet</h3>
<pre><code class="lang-json"><span class="hljs-string">"mcpServers"</span>: {
  <span class="hljs-attr">"aws-documentation"</span>: {
    <span class="hljs-attr">"command"</span>: <span class="hljs-string">"uvx"</span>,
    <span class="hljs-attr">"args"</span>: [<span class="hljs-string">"awslabs.aws-documentation-mcp-server"</span>]
  }
}
</code></pre>
<h2 id="heading-aws-mcp-servers-you-should-use-today">AWS MCP Servers You Should Use Today</h2>
<p>While AWS offers a growing list of servers (including servers for Lambda, S3, and CloudWatch), these three provide the most immediate value for developers and architects.</p>
<h3 id="heading-1-aws-documentation-mcp">1. AWS Documentation MCP</h3>
<p>The <strong>AWS Documentation MCP</strong> exposes AWS documentations via MCP. Instead of the LLM relying on its training data (which might be outdated), this server allows it to fetch the latest official AWS docs in real-time.</p>
<p>This will allow your AI assistant to programatically fetch and read up to date AWS documentations. With this, when learning AWS and troubleshooting any projects, your AI assistant always has access to up to date AWS information. With this you can prevent the AI from hallucinating old API syntax or deprecated service limits.</p>
<h3 id="heading-2-aws-knowledge-mcp">2. AWS Knowledge MCP</h3>
<p>The <strong>AWS Knowledge MCP</strong> is a remote, fully-managed server designed for deep architectural research and regional service availability. It can quickly check which services or specific features (like a specific EC2 instance type) are available in a given AWS Region, provide full stack development guidance and access latest CDK and Cloudfromation documents for better IaC (Infra-as-code) development</p>
<h3 id="heading-3-aws-diagram-mcp">3. AWS Diagram MCP</h3>
<p>The <strong>AWS Diagram MCP</strong> allows your AI to act as a cloud architect. It bridges the gap between a text-based conversation and a visual architecture diagram. It uses the Python <code>diagrams</code> package DSL to generate professional-looking architecture images. It can generate AWS architecture diagrams, sequence diagrams, and flowcharts. You can ask the AI to <em>"draw a highly available web app"</em> and it will generate the code and render the image for you to review.</p>
<p>The shift toward MCP marks a transition from AI assistants "that talk about AWS" to "work on or with AWS." By integrating these three servers you create a comprehensive environment where your AI assistant can research, plan, and visualize your cloud infrastructure in one continuous workflow.</p>
]]></content:encoded></item><item><title><![CDATA[JARK Stack for Gen AI on Kubernetes]]></title><description><![CDATA[You should have heard of LAMP, MEAN, MERN or even JAM stack. But what is this new JARK stack?
Let’s unpack that. JARK stands for Jupyter, Argo, Ray, and Kubernetes. It can help teams launch gen‑AI pipelines in production-ready infrastructure.

Let’s ...]]></description><link>https://blog.ceruleancloud.ca/jark-stack-for-gen-ai-on-kubernetes</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/jark-stack-for-gen-ai-on-kubernetes</guid><category><![CDATA[gen ai]]></category><category><![CDATA[EKS]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Sun, 27 Jul 2025 17:26:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753637097812/7e10e6b4-4337-48a7-8ce9-fec8acff6d4c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You should have heard of LAMP, MEAN, MERN or even JAM stack. But what is this new JARK stack?</p>
<p>Let’s unpack that. JARK stands for <strong>J</strong>upyter, <strong>A</strong>rgo, <strong>R</strong>ay, and <strong>K</strong>ubernetes. It can help teams launch gen‑AI pipelines in production-ready infrastructure.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753631083983/d8c78e59-b5d3-4a76-9b78-e11636f1a547.png" alt class="image--center mx-auto" /></p>
<p>Let’s unpack this further!</p>
<p>The JARK stack brings together four open-source building blocks to manage end‑to‑end generative AI workloads:</p>
<ul>
<li><p><strong>JupyterHub</strong>: Multi-user notebook environment for experimentation, fine‑tuning, and analysis.</p>
</li>
<li><p><strong>Argo Workflows</strong>: YAML‑defined DAG pipelines connecting notebook stages to training, packaging, and serving.</p>
</li>
<li><p><strong>Ray</strong> &amp; <strong>Ray Serve</strong>: Scalable distributed compute across GPU/CPU; Ray Serve handles high‑throughput inference.</p>
</li>
<li><p><strong>Kubernetes (EKS)</strong>: The orchestrator; handles scheduling, scaling, GPU node provisioning (often via Karpenter), multi-tenancy, and resilience.</p>
</li>
</ul>
<p>This stack originated from AWS’s re:Invent 2023 presentation "Deploying Generative Models on Amazon EKS".</p>
<h3 id="heading-why-teams-are-building-jark-on-eks">Why Teams Are Building JARK on EKS</h3>
<h4 id="heading-experimentation-production-in-one-place">Experimentation + Production in One Place</h4>
<p>JupyterHub serves as a collaboration point for data scientists and ML engineers. They can interactively prototype models, fine-turn hyperparameters, and version notebooks—all inside the same Kubernetes cluster used for production workloads.</p>
<h4 id="heading-declarative-pipelines-that-scale">Declarative Pipelines That Scale</h4>
<p>With Argo Workflows, you can "define once, run anywhere." Want to run fine-tuning, validation, packaging, and deployment as steps? Argo manages it as a directed graph, running in containers with GPU scheduling support.</p>
<h4 id="heading-distributed-compute-amp-scalable-serving">Distributed Compute &amp; Scalable Serving</h4>
<p>For model training and inference, Ray lets tasks scale across nodes. Ray Serve adds autoscaling, streaming, and robust APIs for serving large language models or vision engines.</p>
<h4 id="heading-cloud-native-and-cost-aware">Cloud-Native and Cost-Aware</h4>
<p>EKS nodes can autoscale thanks to Karpenter or managed nodegroups. GPU nodes can spin up on demand, minimizing idle costs. Kubernetes also provides namespaces, RBAC, monitoring, and resource isolation.</p>
<h2 id="heading-sample-jark-stack-deployment-with-eks-auto-mode">Sample JARK stack deployment with EKS Auto Mode</h2>
<p>The following is the broad overview of EKS Auto Mode cluster architecture.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753637034004/a8d8d390-9748-4c13-bc98-c5f7805c81d9.jpeg" alt class="image--center mx-auto" /></p>
<p>Deploy EKS Auto Mode Cluster with <code>autoModeConfig</code>:</p>
<pre><code class="lang-plaintext">apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: jark-autocluster
  region: us-west-2
  version: "1.27"
autoModeConfig:
  enabled: true
  nodeRoleARN: arn:aws:iam::&lt;account‑id&gt;:role/EKSAutoNodeRole
  nodePools:
    - general-purpose
    - system
managed: false
vpc:
  nat:
    gateway: Single
</code></pre>
<p>Then:</p>
<pre><code class="lang-plaintext">create cluster -f eks-cluster-aut-mode.yaml
</code></pre>
<p>This provisions cluster where Karpenter auto-provisions node pools and handles upgrades (~every 21 days) automatically.</p>
<h2 id="heading-custom-node-pool-creation">Custom Node Pool Creation</h2>
<p>In Auto Mode, node pools are replaced by provisioners configured via Karpenter. To handle GPU workloads and isolate system pods, define two provisioner CRDs.</p>
<pre><code class="lang-plaintext">apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: jark-gpu
spec:
  requirements:
    - key: "node.kubernetes.io/instance-type"
      operator: In
      values: ["g5.xlarge","g4dn.xlarge"]
    - key: "kubernetes.io/arch"
      operator: In
      values: ["amd64"]
  provider:
    subnetSelector:
      karpenter.sh/discovery: &lt;your-vpc&gt;
    securityGroupSelector:
      kubernetes.io/cluster/jark-autocluster: shared
  taints:
    - key: "ray.io/node-type"
      value: "worker"
      effect: "NoSchedule"
  limits:
    resources:
      cpu: "1000"
      memory: "2000Gi"
  ttlSecondsAfterEmpty: 60
</code></pre>
<p>And CPU/general-purpose:</p>
<pre><code class="lang-plaintext">apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: jark-cpu
spec:
  requirements:
    - key: "node.kubernetes.io/instance-type"
      operator: In
      values: ["m5.xlarge","m5.2xlarge"]
  ttlSecondsAfterEmpty: 30
</code></pre>
<p>Apply them:</p>
<pre><code class="lang-plaintext">bashCopyEditkubectl apply -f gpu-provisioner.yaml
kubectl apply -f cpu-provisioner.yaml
</code></pre>
<h2 id="heading-install-jupyterhub-argo-and-ray">Install JupyterHub, Argo and Ray</h2>
<h3 id="heading-install-jupyterhub">Install JupyterHub</h3>
<p>Use Helm chart from the AWS Data‑on‑EKS blueprint to deploy JupyterHub</p>
<pre><code class="lang-plaintext">helm repo add data-on-eks https://awslabs.github.io/data-on-eks
helm repo update
helm install jhub data-on-eks/jupyterhub \
  --namespace jupyterhub --create-namespace \
  --set singleuser.image.name=jupyter/minimal-notebook \
  --set proxy.service.type=LoadBalancer
</code></pre>
<p>This sets up multi-user JupyterHub with a public LB endpoint .</p>
<h3 id="heading-install-argo-workflows">Install Argo Workflows</h3>
<pre><code class="lang-plaintext">create namespace argo
kubectl apply -n argo -f https://raw.githubusercontent.com/argoproj/argo-workflows/stable/manifests/install.yaml
</code></pre>
<p>Verify argo is deployed.</p>
<pre><code class="lang-plaintext">kubectl get pods -n argo
kubectl port-forward svc/argo-server -n argo 2746:2746
</code></pre>
<h3 id="heading-install-ray-operator-and-ray-serve">Install Ray Operator and Ray Serve</h3>
<pre><code class="lang-plaintext">helm repo add ray https://ray-project.github.io/kuberay-helm/
helm repo update
helm install ray-operator ray/ray-operator --namespace ray-system --create-namespace
</code></pre>
<p>Deploy a Ray cluster manifest (<code>ray-cluster.yaml</code>) to run Serve workers on GPU nodes and Ray head:</p>
<pre><code class="lang-plaintext">apiVersion: ray.io/v1alpha1
kind: RayCluster
metadata:
  name: jark-ray
  namespace: default
spec:
  rayVersion: "2.9.1"
  headGroupSpec:
    template:
      spec:
        containers:
          - name: ray-head
            image: rayproject/ray:latest
            ports:
              - containerPort: 8265
  workerGroupSpecs:
    - groupName: gpu-workers
      minReplicas: 0
      maxReplicas: 2
      rayStartParams: {}
      template:
        spec:
          containers:
            - name: ray-worker
              image: rayproject/ray:latest
              resources:
                limits:
                  nvidia.com/gpu: 1
              args: ["ray", "start", "--address=$(RAY_HEAD_IP):6379"]
          tolerations:
            - key: "ray.io/node-type"
              operator: "Exists"
              effect: "NoSchedule"
</code></pre>
<p>Apply:</p>
<pre><code class="lang-plaintext">kubectl apply -f ray-cluster.yaml
</code></pre>
<h2 id="heading-sample-model-from-hugging-face-amp-serve-inference">Sample Model from Hugging Face &amp; Serve Inference</h2>
<h3 id="heading-create-a-ray-serve-application">Create a Ray Serve application</h3>
<p>In <code>serve_app.py</code>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> ray <span class="hljs-keyword">import</span> serve
<span class="hljs-keyword">from</span> transformers <span class="hljs-keyword">import</span> pipeline

print(<span class="hljs-string">"Loading HuggingFace model..."</span>)
model = pipeline(<span class="hljs-string">"text-generation"</span>, model=<span class="hljs-string">"gpt2"</span>)

serve.start()
<span class="hljs-meta">@serve.deployment(route_prefix="/generate")</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GenModel</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__call__</span>(<span class="hljs-params">self, request</span>):</span>
        data = request.json()
        <span class="hljs-keyword">return</span> {<span class="hljs-string">"result"</span>: model(data[<span class="hljs-string">"prompt"</span>], max_length=<span class="hljs-number">50</span>)[<span class="hljs-number">0</span>][<span class="hljs-string">"generated_text"</span>]}

GenModel.deploy()
</code></pre>
<h3 id="heading-build-docker-image-and-push-to-ecr-inside-a-notebook-or-ci-pipeline">Build Docker image and push to ECR (inside a notebook or CI pipeline)</h3>
<pre><code class="lang-plaintext">bashCopyEditdocker build -t ghserve:latest -f Dockerfile .
aws ecr create-repository --repository-name ghserve
docker tag ghserve:latest &lt;account&gt;.dkr.ecr.us-west-2.amazonaws.com/ghserve:latest
docker push &lt;URI&gt;/ghserve:latest
</code></pre>
<h3 id="heading-deploy-via-kubernetes-manifest-ray-serve-appyaml">Deploy via Kubernetes manifest <code>ray-serve-app.yaml</code>:</h3>
<pre><code class="lang-plaintext">apiVersion: apps/v1
kind: Deployment
metadata:
  name: serve-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: serve-app
  template:
    metadata:
      labels:
        app: serve-app
    spec:
      containers:
      - name: serve-app
        image: &lt;URI&gt;/ghserve:latest
        ports:
        - containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
  name: serve-service
spec:
  type: LoadBalancer
  selector:
    app: serve-app
  ports:
    - port: 80
      targetPort: 8000
</code></pre>
<p>Apply:</p>
<pre><code class="lang-plaintext">kubectl apply -f ray-serve-app.yaml
</code></pre>
<h3 id="heading-test-inference">Test inference</h3>
<pre><code class="lang-plaintext">curl http://$(kubectl get svc serve-service -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')/generate \
  -H "Content-Type: application/json" \
  -d '{"prompt":"Hello world"}'
</code></pre>
<p>Now, In summary, we have:</p>
<ul>
<li><p>An <strong>EKS cluster in Auto Mode</strong>, scaling nodes automatically via Karpenter.</p>
</li>
<li><p><strong>Custom provisioners</strong> for GPU worker isolation and cost‑effective scaling.</p>
</li>
<li><p>A functional <strong>JARK stack</strong>: JupyterHub, Argo, and Ray Serve.</p>
</li>
<li><p>A <strong>sample Hugging Face model served via Ray</strong>, with inference exposed through LoadBalancer.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Build an AI powered customer service voice agent on AWS]]></title><description><![CDATA[A few weeks ago, I found myself in Vegas, trying to update a dinner reservation on a packed Saturday night. Calling the restaurant was useless—there was no chance of getting through. That got me thinking: how quickly could I spin up an AI-powered res...]]></description><link>https://blog.ceruleancloud.ca/build-an-ai-powered-customer-service-voice-agent-on-aws</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/build-an-ai-powered-customer-service-voice-agent-on-aws</guid><category><![CDATA[AWS]]></category><category><![CDATA[AI]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Sun, 23 Mar 2025 16:48:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742748464534/0507ffbc-3957-40bb-b701-2af45b6efb44.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A few weeks ago, I found myself in Vegas, trying to update a dinner reservation on a packed Saturday night. Calling the restaurant was useless—there was no chance of getting through. That got me thinking: how quickly could I spin up an AI-powered reservation assistant?</p>
<p>So, I put it to the test. In under an hour, I built a crude proof of concept (PoC) using AWS services. The setup was simple:</p>
<ul>
<li><p><strong>Amazon Connect</strong> to handle incoming calls</p>
</li>
<li><p><strong>Amazon Lex</strong> for voice interaction and capturing reservation details</p>
</li>
<li><p><strong>AWS Lambda</strong> to process and store the reservation</p>
</li>
<li><p><strong>Amazon DynamoDB</strong> for storing reservations</p>
</li>
<li><p><strong>Amazon SES</strong> to send confirmation emails</p>
</li>
</ul>
<p>This isn’t about cutting-edge AI. It’s about solving real problems—fast. AI isn’t useful unless it’s applied to the right problem, and once you do that, impact follows almost instantly.</p>
<p>Now, let’s dive into how I built it.</p>
<h2 id="heading-architecture"><strong>Architecture</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742563599444/6223826e-03b3-47a3-b0c3-9516aa73c87e.jpeg" alt class="image--center mx-auto" /></p>
<h2 id="heading-setting-up-amazon-connect-for-call-routing"><strong>Setting Up Amazon Connect for Call Routing</strong></h2>
<h3 id="heading-create-an-amazon-connect-instance"><strong>Create an Amazon Connect Instance</strong></h3>
<ol>
<li><p>Go to the <strong>AWS Console</strong> → <strong>Amazon Connect</strong> → <strong>Create an instance</strong>.</p>
</li>
<li><p>Set up an administrator account and choose a name for the instance.</p>
</li>
<li><p>Follow the instance creation flow to claim a phone number and use it for incoming calls</p>
</li>
</ol>
<p>Now, whenever someone calls this number, we can define what happens next using a <strong>contact flow</strong>.</p>
<h3 id="heading-configure-a-contact-flow-to-invoke-lex"><strong>Configure a Contact Flow to Invoke Lex</strong></h3>
<p>A <strong>contact flow</strong> is a set of actions Amazon Connect follows when handling a call. Under bot create a Lex bot. After configuring the bot, create an “intent” to capture user request to make reservations.</p>
<p>Additionally, the call flow I configured a welcome prompt and terminating the call after capturing the required information</p>
<h2 id="heading-creating-an-amazon-lex-bot-for-reservations"><strong>Creating an Amazon Lex Bot for Reservations</strong></h2>
<h3 id="heading-define-the-lex-bot-and-intent"><strong>Define the Lex Bot and Intent</strong></h3>
<p>Amazon Lex is AWS’s conversational AI service, handling both text and voice inputs.</p>
<ol>
<li><p>In <strong>Amazon Lex</strong>, create a new bot called <code>ReservationBot</code>.</p>
</li>
<li><p>Under <strong>Intents</strong>, create a new intent called <code>MakeReservation</code>.</p>
</li>
<li><p>Add sample utterances like:</p>
<ul>
<li><p><em>I want to make a reservation</em></p>
</li>
<li><p><em>Can I book a table for tonight?</em></p>
</li>
<li><p><em>Reserve a table for two at 7 PM</em></p>
</li>
</ul>
</li>
</ol>
<p><strong>Add Slots to Capture User Data</strong></p>
<p>Slots are variables that store user inputs. Add the following slots to <code>MakeReservation</code>:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Slot Name</td><td>Type</td><td>Prompt Example</td><td>Required?</td></tr>
</thead>
<tbody>
<tr>
<td><code>firstName</code></td><td>AMAZON.FirstName</td><td>What name should I book under?</td><td>Yes</td></tr>
<tr>
<td><code>phoneNumber</code></td><td>AMAZON.PhoneNumber</td><td>Can I get your phone number?</td><td>Yes</td></tr>
<tr>
<td><code>partySize</code></td><td>AMAZON.Number</td><td>How many people are in your party?</td><td>Yes</td></tr>
<tr>
<td><code>date</code></td><td><a target="_blank" href="http://AMAZON.Date">AMAZON.Date</a></td><td>What date do you need the reservation for?</td><td>Yes</td></tr>
<tr>
<td><code>time</code></td><td>AMAZON.Time</td><td>What time would you like the reservation?</td><td>Yes</td></tr>
<tr>
<td><code>confirmation</code></td><td>AMAZON.YesNo</td><td>Should I confirm your reservation?</td><td>Yes</td></tr>
</tbody>
</table>
</div><p><strong>Configure Fulfillment with AWS Lambda</strong></p>
<p>Once Lex collects user input, we need to process the reservation and store it in DynamoDB.</p>
<ol>
<li><p>In Lex, go to the <strong>Fulfillment</strong> section.</p>
</li>
<li><p>Select <strong>AWS Lambda function</strong> and create a new function.</p>
</li>
<li><p>Use the following Python code:</p>
</li>
</ol>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">import</span> uuid

dynamodb = boto3.resource(<span class="hljs-string">"dynamodb"</span>)
table = dynamodb.Table(<span class="hljs-string">"Reservations"</span>)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    slots = event[<span class="hljs-string">"currentIntent"</span>][<span class="hljs-string">"slots"</span>]

    reservation_id = str(uuid.uuid4())
    reservation_data = {
        <span class="hljs-string">"ReservationID"</span>: reservation_id,
        <span class="hljs-string">"FirstName"</span>: slots[<span class="hljs-string">"firstName"</span>],
        <span class="hljs-string">"PhoneNumber"</span>: slots[<span class="hljs-string">"phoneNumber"</span>],
        <span class="hljs-string">"PartySize"</span>: slots[<span class="hljs-string">"partySize"</span>],
        <span class="hljs-string">"Date"</span>: slots[<span class="hljs-string">"date"</span>],
        <span class="hljs-string">"Time"</span>: slots[<span class="hljs-string">"time"</span>]
    }

    table.put_item(Item=reservation_data)

    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">"dialogAction"</span>: {
            <span class="hljs-string">"type"</span>: <span class="hljs-string">"Close"</span>,
            <span class="hljs-string">"fulfillmentState"</span>: <span class="hljs-string">"Fulfilled"</span>,
            <span class="hljs-string">"message"</span>: {
                <span class="hljs-string">"contentType"</span>: <span class="hljs-string">"PlainText"</span>,
                <span class="hljs-string">"content"</span>: <span class="hljs-string">f"Your reservation is confirmed, <span class="hljs-subst">{slots[<span class="hljs-string">'firstName'</span>]}</span>! Your confirmation ID is <span class="hljs-subst">{reservation_id}</span>."</span>
            }
        }
    }
</code></pre>
<p>This function:<br />✅ Extracts user inputs from Lex<br />✅ Generates a unique reservation ID<br />✅ Stores the reservation in DynamoDB<br />✅ Returns a confirmation message</p>
<h3 id="heading-sending-confirmation-emails-with-amazon-ses"><strong>Sending Confirmation Emails with Amazon SES</strong></h3>
<p>Once the reservation is stored, we want to send a confirmation email. Modify the Lambda function to include this:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_email</span>(<span class="hljs-params">to_address, first_name, reservation_id</span>):</span>
    subject = <span class="hljs-string">"Your Reservation is Confirmed!"</span>
    body = <span class="hljs-string">f"Hello <span class="hljs-subst">{first_name}</span>,\n\nYour reservation is confirmed. Your confirmation ID is <span class="hljs-subst">{reservation_id}</span>.\n\nThank you!"</span>

    response = ses.send_email(
        Source=<span class="hljs-string">"your-email@example.com"</span>,
        Destination={<span class="hljs-string">"ToAddresses"</span>: [to_address]},
        Message={
            <span class="hljs-string">"Subject"</span>: {<span class="hljs-string">"Data"</span>: subject},
            <span class="hljs-string">"Body"</span>: {<span class="hljs-string">"Text"</span>: {<span class="hljs-string">"Data"</span>: body}}
        }
    )
    <span class="hljs-keyword">return</span> response
</code></pre>
<p>Update the <code>lambda_handler</code> function to call <code>send_email()</code>:</p>
<pre><code class="lang-python">send_email(slots[<span class="hljs-string">"phoneNumber"</span>] + <span class="hljs-string">"@example.com"</span>, slots[<span class="hljs-string">"firstName"</span>], reservation_id)
</code></pre>
<p>Now, once a reservation is made, an email confirmation is sent to the customer.</p>
<h2 id="heading-testing-the-full-setup"><strong>Testing the Full Setup</strong></h2>
<p><strong>Test the Call Flow in Amazon Connect</strong></p>
<ul>
<li><p>Call the phone number assigned to Amazon Connect.</p>
</li>
<li><p>Try making a reservation by voice.</p>
</li>
<li><p>Ensure the data is stored in DynamoDB and a confirmation email is sent.</p>
</li>
</ul>
<p>This setup isn’t complex, and that’s the beauty of it. In under an hour, we built a fully functional AI-powered reservation system using AWS services. The key takeaways:</p>
<p>✅ <strong>Amazon Connect + Lex</strong> makes voice call automation easy.<br />✅ <strong>Lambda + DynamoDB</strong> handles backend processing and storage.<br />✅ <strong>Amazon SES</strong> automates email confirmations.</p>
<p>This solution can be expanded in many ways—multi-language support, SMS confirmations via SNS, or even integrating with restaurant POS systems. But the core idea remains: AI is most effective when applied to the right problems.</p>
]]></content:encoded></item><item><title><![CDATA[Building a Live Stream Infrastructure on AWS]]></title><description><![CDATA[A few days ago, Netflix made history by streaming the Screen Actors Guild (SAG) Awards live for the first time. This marked a major shift in how award shows and other large-scale events are broadcasted, proving that live streaming is no longer just f...]]></description><link>https://blog.ceruleancloud.ca/building-a-live-stream-infrastructure-on-aws</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/building-a-live-stream-infrastructure-on-aws</guid><category><![CDATA[AWS architecture]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Fri, 28 Feb 2025 19:18:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742748611013/cd43e071-daf6-41c3-a9d8-0fc2f858cd1d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A few days ago, Netflix made history by streaming the <strong>Screen Actors Guild (SAG) Awards</strong> live for the first time. This marked a major shift in how award shows and other large-scale events are broadcasted, proving that live streaming is no longer just for sports and gaming—it's now a mainstream entertainment medium. This got me thinking: <strong>What does it take to build a scalable, high-quality live streaming architecture?</strong></p>
<p>In this blog, I’ll break down how AWS provides a robust solution for live streaming, using a combination of services like <strong>AWS Elemental MediaLive, MediaPackage, Amazon S3, and CloudFront</strong> to ensure seamless content delivery. <strong>Now, have in mind - these services are purpose built by AWS to serve such media usecases.</strong></p>
<p>Whether you're streaming a major award show, a corporate event, or even a personal live broadcast, understanding the building blocks of AWS live streaming can help you create a reliable and scalable streaming experience.</p>
<p>Let’s dive into how each AWS service plays a role in making this happen.</p>
<h2 id="heading-architecture">Architecture</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740769047075/e0aabe3f-be94-4f2a-a282-ffc4d38d038c.jpeg" alt class="image--center mx-auto" /></p>
<p>This architecture is for <strong>Live Streaming on AWS</strong>, leveraging various AWS services to efficiently deliver video content to end-user devices. Here's a detailed breakdown of the services involved and the overall workflow:</p>
<p>1. <strong>Video Source</strong></p>
<ul>
<li>This represents the camera or any other video capture device that provides the live video feed. It acts as the input source for the streaming process.</li>
</ul>
<p>2. <strong>AWS Elemental Link</strong></p>
<ul>
<li><p><strong>Role:</strong> AWS Elemental Link is a device that connects the video source (like a camera or production equipment) to AWS Elemental MediaLive.</p>
</li>
<li><p><strong>Function:</strong> It encodes and streams the video content to AWS, ensuring high-quality transmission. Link supports standard video inputs and outputs that can easily be connected to production systems.</p>
</li>
<li><p><strong>Benefit:</strong> It provides a reliable and scalable way to ingest live video streams, bridging the gap between the on-premise hardware and AWS cloud services.</p>
</li>
</ul>
<p>3. <strong>AWS Elemental MediaLive</strong></p>
<ul>
<li><p><strong>Role:</strong> MediaLive is a broadcast-grade live video processing service. It ingests the video from Elemental Link and processes it to create the live stream.</p>
</li>
<li><p><strong>Function:</strong> It transcodes the video into different formats for various devices and resolutions. MediaLive is capable of adaptive bitrate streaming, ensuring the best quality video delivery under varying network conditions.</p>
</li>
<li><p><strong>Benefit:</strong> AWS Elemental MediaLive is highly scalable, ensuring that the stream can reach a wide audience without degrading the quality. It also integrates with other AWS services to enhance performance and security.</p>
</li>
</ul>
<p>4. <strong>AWS Elemental MediaPackage</strong></p>
<ul>
<li><p><strong>Role:</strong> MediaPackage is used to create live streaming packages, enabling the stream to be formatted and delivered across multiple devices and platforms.</p>
</li>
<li><p><strong>Function:</strong> It packages the content delivered by MediaLive into formats like HLS (HTTP Live Streaming) and DASH (Dynamic Adaptive Streaming over HTTP) for device compatibility. MediaPackage also supports features like encryption and DRM (Digital Rights Management).</p>
</li>
<li><p><strong>Benefit:</strong> This service helps deliver high-quality video to devices regardless of their platform, and ensures smooth, secure, and efficient streaming. It allows for failover strategies, ensuring reliability.</p>
</li>
</ul>
<p>5. <strong>Amazon S3</strong></p>
<ul>
<li><p><strong>Role:</strong> Amazon Simple Storage Service (S3) is used to store the video files, including both live and archived content.</p>
</li>
<li><p><strong>Function:</strong> MediaPackage stores the processed live streams in S3, ensuring that the video content is secure, scalable, and easily accessible for future use or playback.</p>
</li>
<li><p><strong>Benefit:</strong> S3 provides durable and low-latency storage, making it a reliable place for hosting video content for both live and on-demand applications.</p>
</li>
</ul>
<p>6. <strong>Amazon CloudFront</strong></p>
<ul>
<li><p><strong>Role:</strong> CloudFront is a global content delivery network (CDN) that accelerates the delivery of content to end-user devices.</p>
</li>
<li><p><strong>Function:</strong> CloudFront caches content from Amazon S3, ensuring that the live video feed is quickly delivered to users regardless of their geographical location.</p>
</li>
<li><p><strong>Benefit:</strong> CloudFront helps reduce latency by delivering content from the closest edge location to the user. It optimizes the delivery of media content, ensuring seamless streaming across various devices.</p>
</li>
</ul>
<p>7. <strong>End User Devices</strong></p>
<ul>
<li><p><strong>Role:</strong> These are the devices used by the viewers to watch the live stream.</p>
</li>
<li><p><strong>Function:</strong> End-user devices like smartphones, tablets, desktops, and smart TVs consume the live stream via their respective media players or apps.</p>
</li>
<li><p><strong>Benefit:</strong> CloudFront ensures that the video stream is optimized for these devices, regardless of the bandwidth or device specifications.</p>
</li>
</ul>
<h3 id="heading-how-this-architecture-works"><strong>How This Architecture Works</strong></h3>
<ol>
<li><p><strong>Capture &amp; Transmission:</strong> The video source captures the live feed, and AWS Elemental Link encodes and sends this video stream to MediaLive.</p>
</li>
<li><p><strong>Processing &amp; Transcoding:</strong> MediaLive processes the incoming stream, converting it into different formats and resolutions for adaptive streaming.</p>
</li>
<li><p><strong>Packaging &amp; Security:</strong> MediaPackage formats the video into compatible packages (HLS/DASH) for delivery across different platforms, while also providing encryption and security features.</p>
</li>
<li><p><strong>Storage:</strong> Processed streams are stored in Amazon S3 for archival purposes.</p>
</li>
<li><p><strong>Distribution:</strong> CloudFront delivers the video to end-user devices with low latency, ensuring a smooth viewing experience for a global audience.</p>
</li>
</ol>
<h3 id="heading-key-benefits-of-this-architecture"><strong>Key Benefits of This Architecture</strong></h3>
<ul>
<li><p><strong>Scalability:</strong> AWS services like MediaLive, MediaPackage, and CloudFront automatically scale based on viewer demand, providing a reliable service even during high traffic.</p>
</li>
<li><p><strong>Global Reach:</strong> CloudFront’s edge locations ensure that the video stream is delivered to users worldwide with minimal latency.</p>
</li>
<li><p><strong>Security:</strong> Encryption and access control are built into the architecture, ensuring that content is secure both in transit and at rest.</p>
</li>
<li><p><strong>Cost Efficiency:</strong> This setup leverages the pay-as-you-go pricing model, which means you only pay for the services you use, making it cost-effective for both small and large-scale live streaming events.</p>
</li>
</ul>
<p>That’s it! We just figured how to reliable live stream via the AWS managed infrastructure and services to end-user devices.</p>
]]></content:encoded></item><item><title><![CDATA[Serve a HTML Resume Site via a Docker Container]]></title><description><![CDATA[In this blog, we'll walk you through the steps to serve a simple HTML website using Apache2 in a Docker container. This guide assumes you have an Ubuntu environment with Docker installed.
Assumptions

Ubuntu Environment: Ensure you're using an Ubuntu...]]></description><link>https://blog.ceruleancloud.ca/serve-a-html-resume-site-via-a-docker-container</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/serve-a-html-resume-site-via-a-docker-container</guid><category><![CDATA[containers]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Fri, 28 Feb 2025 18:53:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742530339464/776417d4-1dcb-4dad-9a58-4b7e78e63500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this blog, we'll walk you through the steps to serve a simple HTML website using Apache2 in a Docker container. This guide assumes you have an Ubuntu environment with Docker installed.</p>
<p><strong>Assumptions</strong></p>
<ul>
<li><p><strong>Ubuntu Environment</strong>: Ensure you're using an Ubuntu-based system.</p>
</li>
<li><p><strong>Docker Installed</strong>: Verify Docker is installed and running using the command <code>docker --version</code>.</p>
</li>
</ul>
<p><strong>Create the HTML File</strong></p>
<p>Create a directory to store your HTML file and your Dockerfile.</p>
<p><strong><em>Dockerfile will contain instructions on how to build the image.</em></strong></p>
<pre><code class="lang-plaintext">mkdir my-resume-site
cd my-resume-site
vi index.html
</code></pre>
<p>Create the <code>index.html</code> file inside this directory with the following content:</p>
<pre><code class="lang-xml">htmlCopy code<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>My Resume<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
        <span class="hljs-selector-tag">body</span> {
            <span class="hljs-attribute">font-family</span>: Arial, sans-serif;
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span>;
            <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.6</span>;
        }
        <span class="hljs-selector-tag">h1</span> {
            <span class="hljs-attribute">text-align</span>: center;
        }
        <span class="hljs-selector-class">.section</span> {
            <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>;
        }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>John Doe<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Email: john.doe@example.com | Phone: (123) 456-7890<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">hr</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Professional Summary<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Motivated and detail-oriented professional with experience in web development, system administration, and cloud technologies. Passionate about delivering impactful solutions and learning new technologies.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Experience<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Software Engineer, ABC Corp<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Jan 2020 - Present<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Developed and maintained scalable web applications using modern frameworks.<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Implemented CI/CD pipelines to automate deployments.<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Education<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Bachelor of Science in Computer Science<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>XYZ University, 2019<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p><strong>Create a Dockerfile</strong></p>
<p>To serve the HTML site, create a <code>Dockerfile</code> to set up the Apache2 web server.</p>
<pre><code class="lang-plaintext">vi Dockerfile
</code></pre>
<p>Add the following content to the file and save it.</p>
<pre><code class="lang-plaintext"># Use the official Apache2 image as the base
FROM httpd:2.4

# Copy the HTML file into the container
COPY ./index.html /usr/local/apache2/htdocs/
</code></pre>
<hr />
<p><strong>Build the Docker Image</strong></p>
<p>Build the Docker image using the <code>Dockerfile</code>.</p>
<pre><code class="lang-plaintext">docker build -t my-resume-site .
</code></pre>
<p>This command tags the image as <code>my-resume-site</code> .</p>
<p>The <code>.</code> in the command - points to the current directory. Docker will automatically know to look for a file named <code>Dockerfile</code></p>
<p><strong>Run the Docker Container</strong></p>
<p>Run the container and map it to port 80 on your host machine.</p>
<pre><code class="lang-plaintext">docker run -d -p 80:80 --name resume-site my-resume-site
</code></pre>
<ul>
<li><p><code>-d</code>: Runs the container in detached mode.</p>
</li>
<li><p><code>-p 80:80</code>: Maps port 80 of the host to port 80 of the container.</p>
</li>
<li><p><code>--name resume-site</code>: Names the container <code>resume-site</code>.</p>
</li>
</ul>
<p><strong>Access the Site</strong></p>
<p>Open your browser and navigate to <code>http://localhost:80</code>. You should see your resume displayed.</p>
<p><strong>Verify the Container is Running</strong></p>
<p>Check if the container is running:</p>
<pre><code class="lang-plaintext">docker ps
</code></pre>
<p>The output should list the <code>resume-site</code> container.</p>
<p><strong>Cleanup - Stop and Remove the Container</strong></p>
<p>To stop the container:</p>
<pre><code class="lang-plaintext">docker stop resume-site
</code></pre>
<p>To remove the container:</p>
<pre><code class="lang-plaintext">docker rm resume-site
</code></pre>
<p><strong>Conclusion</strong></p>
<p>You've successfully served a simple HTML resume site using Apache2 in a Docker container!</p>
]]></content:encoded></item><item><title><![CDATA[Git Introduction]]></title><description><![CDATA[Git is the most popular version control system used by developers around the world. It helps track changes in your code, collaborate with others, and manage project versions efficiently. Whether you're working solo or as part of a team, Git ensures y...]]></description><link>https://blog.ceruleancloud.ca/git-introduction</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/git-introduction</guid><category><![CDATA[Git]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Wed, 01 Jan 2025 19:23:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742530590402/27085b15-79c6-4701-b034-0b5b6c2b337a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Git is the most popular version control system used by developers around the world. It helps track changes in your code, collaborate with others, and manage project versions efficiently. Whether you're working solo or as part of a team, Git ensures your work is organized and never accidentally lost.</p>
<p>In this blog, we'll walk you through the following:</p>
<ul>
<li><p><strong>Understanding Git basics.</strong></p>
</li>
<li><p><strong>Installing Git on Ubuntu.</strong></p>
</li>
<li><p><strong>Creating a GitHub repository.</strong></p>
</li>
<li><p><strong>Pushing the</strong> <code>index.html</code> <strong>file</strong> <a target="_blank" href="https://blog.ceruleancloud.ca/deploy-static-resume-on-an-apache-server"><strong>that was shown in this blog</strong></a> <strong>to a GitHub repository.</strong></p>
</li>
</ul>
<h3 id="heading-introduction-to-git"><strong>Introduction to Git</strong></h3>
<h3 id="heading-what-is-git"><strong>What is Git?</strong></h3>
<p>Git is a distributed version control system designed to manage changes in files, especially code, efficiently. It allows developers to:</p>
<ul>
<li><p>Track changes over time.</p>
</li>
<li><p>Collaborate with multiple people without conflicts.</p>
</li>
<li><p>Revert to previous versions if needed.</p>
</li>
</ul>
<h3 id="heading-why-use-git"><strong>Why Use Git?</strong></h3>
<ul>
<li><p><strong>Versioning:</strong> Always have access to previous versions of your code.</p>
</li>
<li><p><strong>Collaboration:</strong> Multiple developers can work on the same project simultaneously.</p>
</li>
<li><p><strong>Backup:</strong> Your codebase is stored safely in remote repositories like GitHub.</p>
</li>
</ul>
<h3 id="heading-installing-git-on-ubuntu"><strong>Installing Git on Ubuntu</strong></h3>
<p>To use Git, you need to install and configure it on your machine.</p>
<p>To follow along with the rest of this blog, I am assuming the following:</p>
<ul>
<li><p><strong>You have a</strong> <a target="_blank" href="https://github.com/"><strong>Github</strong></a> <strong>account</strong></p>
</li>
<li><p><strong>You have a working Ubuntu environment</strong></p>
</li>
</ul>
<p><strong>Update the Package Manager</strong></p>
<p>Open your terminal and update the package manager to ensure you have the latest package information:</p>
<pre><code class="lang-plaintext">sudo apt update
</code></pre>
<p><strong>Install Git</strong></p>
<p>Install Git using the following command:</p>
<pre><code class="lang-plaintext">sudo apt install git
</code></pre>
<p><strong>Verify the Installation</strong></p>
<p>Check if Git is installed correctly by running:</p>
<pre><code class="lang-plaintext">git --version
</code></pre>
<p>You should see something like:</p>
<pre><code class="lang-plaintext">git version 2.x.x
</code></pre>
<p><strong>Configure Git</strong></p>
<p>Set up your identity, which will be attached to your commits. This will be your github password and email.</p>
<pre><code class="lang-plaintext">git config --global user.name "Your Name"  
git config --global user.email "youremail@example.com"
</code></pre>
<p>To confirm your configuration, run:</p>
<pre><code class="lang-plaintext">git config --list
</code></pre>
<h3 id="heading-creating-a-github-repository"><strong>Creating a GitHub Repository</strong></h3>
<p><strong>GitHub is a cloud-based service that hosts Git repositories. Here’s how to create one:</strong></p>
<p><strong>Log in to GitHub</strong></p>
<p>Go to Github and log in or sign up for an account.</p>
<p><strong>Create a New Repository</strong></p>
<p>Click on the <strong>+</strong> icon in the top-right corner and select <strong>New Repository</strong>.</p>
<ol>
<li><p>Provide a repository name (e.g., <code>my-resume-site</code>).</p>
</li>
<li><p>Choose visibility:</p>
<ul>
<li><p><strong>Public:</strong> Anyone can view it.</p>
</li>
<li><p><strong>Private:</strong> Only you and your collaborators can view it.<br />  <strong><em>PS: I recommend using Private</em></strong></p>
</li>
</ul>
</li>
<li><p>Check “Initialize this repository with a README” option.</p>
</li>
<li><p>Click <strong>Create repository</strong>.<strong>4. Adding and Pushing Your</strong> <code>index.html</code> File</p>
</li>
</ol>
<p>Now that your GitHub repository is ready, let’s add and push your <code>index.html</code> file to it.</p>
<h3 id="heading-create-a-local-project-directory"><strong>Create a Local Project Directory</strong></h3>
<ol>
<li><p>Move to your home directory</p>
<pre><code class="lang-plaintext"> cd ~
</code></pre>
</li>
<li><p>Create a new folder for your project and move into it:</p>
<pre><code class="lang-plaintext"> mkdir my-resume-site 
 cd my-resume site
</code></pre>
</li>
</ol>
<h3 id="heading-create-the-indexhtml-file"><strong>Create the</strong> <code>index.html</code> File</h3>
<ol>
<li><p>Create a simple <code>index.html</code> file:</p>
<pre><code class="lang-plaintext"> vi index.html
</code></pre>
<p> Copy paste the index.html file contents from <a target="_blank" href="https://blog.ceruleancloud.ca/deploy-static-resume-on-an-apache-server">this blog</a> to this file and save it.</p>
</li>
<li><p>Confirm file exists</p>
<pre><code class="lang-plaintext"> cat index.html
</code></pre>
</li>
</ol>
<h3 id="heading-initialize-git"><strong>Initialize Git</strong></h3>
<p>Initialize Git in your project directory:</p>
<pre><code class="lang-plaintext">git init
</code></pre>
<h3 id="heading-stage-your-file"><strong>Stage Your File</strong></h3>
<p>Add the <code>index.html</code> file to Git’s staging area:</p>
<pre><code class="lang-plaintext">git add index.html
</code></pre>
<h3 id="heading-commit-your-changes"><strong>Commit Your Changes</strong></h3>
<p>Save your changes to Git with a meaningful message:</p>
<pre><code class="lang-plaintext">git commit -m "Initial commit: Add index.html"
</code></pre>
<h3 id="heading-link-to-your-github-repository"><strong>Link to Your GitHub Repository</strong></h3>
<p>Link your local repository to the GitHub repository you created earlier. Replace <code>&lt;yourusername&gt;</code> and <code>&lt;reponame&gt;</code> with your GitHub username and repository name:</p>
<pre><code class="lang-plaintext">git remote add origin https://github.com/&lt;yourusername&gt;/my-resume-site.git
</code></pre>
<h3 id="heading-push-your-code-to-github"><strong>Push Your Code to GitHub</strong></h3>
<p>Push your code to the main branch of your GitHub repository:</p>
<pre><code class="lang-plaintext">git branch -M main  
git push -u origin main
</code></pre>
<h3 id="heading-recap-on-what-we-did-so-far">Recap on what we did so far</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734801058605/5785230a-2b03-4db8-885d-32abc627cf17.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-verifying-your-upload"><strong>Verifying Your Upload</strong></h3>
<ol>
<li><p>Open your GitHub repository in a browser.</p>
</li>
<li><p>You should see your <code>index.html</code> file.</p>
</li>
<li><p>Click on the file to view its contents.</p>
</li>
</ol>
<h3 id="heading-going-further"><strong>Going further</strong></h3>
<p>Now that your code is on GitHub, you can:</p>
<ul>
<li><p>Update the file and push changes using <code>git add</code>, <code>git commit</code>, and <code>git push</code>.</p>
</li>
<li><p>Clone the repository to other machines using <code>git clone</code>.</p>
</li>
<li><p>Collaborate with others by inviting them to your repository.</p>
</li>
</ul>
<h3 id="heading-git-cheatsheet">Git Cheatsheet</h3>
<p>The below table shows most commands that a developer would use in development while using Git.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Command</strong></td><td><strong>Description</strong></td><td><strong>Example Usage</strong></td></tr>
</thead>
<tbody>
<tr>
<td><code>git init</code></td><td>Initialize a new Git repository</td><td><code>git init</code></td></tr>
<tr>
<td><code>git clone &lt;repo_url&gt;</code></td><td>Clone a repository</td><td><code>git clone</code> <a target="_blank" href="https://github.com/user/repo.git"><code>https://github.com/user/repo.git</code></a></td></tr>
<tr>
<td><code>git status</code></td><td>Show the status of your working directory</td><td><code>git status</code></td></tr>
<tr>
<td><code>git add &lt;file&gt;</code></td><td>Stage changes to be committed</td><td><code>git add file.txt</code></td></tr>
<tr>
<td><code>git add .</code></td><td>Stage all changes in the current directory</td><td><code>git add .</code></td></tr>
<tr>
<td><code>git commit -m "&lt;message&gt;"</code></td><td>Commit staged changes with a message</td><td><code>git commit -m "Added new feature"</code></td></tr>
<tr>
<td><code>git push</code></td><td>Push commits to the remote repository</td><td><code>git push origin main</code></td></tr>
<tr>
<td><code>git pull</code></td><td>Fetch and merge changes from the remote</td><td><code>git pull origin main</code></td></tr>
<tr>
<td><code>git branch</code></td><td>List branches</td><td><code>git branch</code></td></tr>
<tr>
<td><code>git branch &lt;branch_name&gt;</code></td><td>Create a new branch</td><td><code>git branch feature-branch</code></td></tr>
<tr>
<td><code>git checkout &lt;branch&gt;</code></td><td>Switch to a branch</td><td><code>git checkout feature-branch</code></td></tr>
<tr>
<td><code>git checkout -b &lt;branch&gt;</code></td><td>Create and switch to a new branch</td><td><code>git checkout -b feature-branch</code></td></tr>
<tr>
<td><code>git merge &lt;branch&gt;</code></td><td>Merge a branch into the current branch</td><td><code>git merge feature-branch</code></td></tr>
<tr>
<td><code>git log</code></td><td>Show commit history</td><td><code>git log</code></td></tr>
<tr>
<td><code>git log --oneline</code></td><td>Show concise commit history</td><td><code>git log --oneline</code></td></tr>
<tr>
<td><code>git diff</code></td><td>Show changes in tracked files</td><td><code>git diff</code></td></tr>
<tr>
<td><code>git diff &lt;branch1&gt; &lt;branch2&gt;</code></td><td>Compare two branches</td><td><code>git diff main feature-branch</code></td></tr>
<tr>
<td><code>git stash</code></td><td>Temporarily save changes without committing</td><td><code>git stash</code></td></tr>
<tr>
<td><code>git stash pop</code></td><td>Apply the last stashed changes</td><td><code>git stash pop</code></td></tr>
<tr>
<td><code>git reset &lt;file&gt;</code></td><td>Unstage a file</td><td><code>git reset file.txt</code></td></tr>
<tr>
<td><code>git reset --hard &lt;commit&gt;</code></td><td>Reset working directory to a specific commit</td><td><code>git reset --hard abc1234</code></td></tr>
<tr>
<td><code>git remote -v</code></td><td>Show remote repositories</td><td><code>git remote -v</code></td></tr>
<tr>
<td><code>git remote add &lt;name&gt; &lt;url&gt;</code></td><td>Add a remote repository</td><td><code>git remote add origin</code> <a target="_blank" href="https://github.com/user/repo.git"><code>https://github.com/user/repo.git</code></a></td></tr>
<tr>
<td><code>git tag &lt;tag_name&gt;</code></td><td>Create a tag for a specific commit</td><td><code>git tag v1.0.0</code></td></tr>
<tr>
<td><code>git fetch</code></td><td>Fetch changes from remote without merging</td><td><code>git fetch origin</code></td></tr>
<tr>
<td><code>git rebase &lt;branch&gt;</code></td><td>Reapply commits on top of another branch</td><td><code>git rebase main</code></td></tr>
<tr>
<td><code>git cherry-pick &lt;commit&gt;</code></td><td>Apply a specific commit to the current branch</td><td><code>git cherry-pick abc1234</code></td></tr>
</tbody>
</table>
</div><p>By mastering Git, you’ll streamline your development workflow and enhance collaboration on any project.</p>
<p>Happy coding!</p>
]]></content:encoded></item><item><title><![CDATA[Kubernetes Introduction]]></title><description><![CDATA[In modern application deployment, Kubernetes has emerged as the go-to platform for container orchestration. Whether you’re deploying microservices, scaling workloads, or managing clusters, Kubernetes offers a robust framework to simplify these tasks....]]></description><link>https://blog.ceruleancloud.ca/kubernetes-introduction</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/kubernetes-introduction</guid><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Sun, 29 Dec 2024 00:26:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742530732348/920b755e-0fba-4e70-81a7-595f5075d2fb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In modern application deployment, <a target="_blank" href="https://kubernetes.io/">Kubernetes</a> has emerged as the go-to platform for container orchestration. Whether you’re deploying microservices, scaling workloads, or managing clusters, Kubernetes offers a robust framework to simplify these tasks. Let’s dive into the basics of Kubernetes and understand its core concepts.</p>
<p>Before you begin:</p>
<p>In order to understand Kubernetes, you should be familiar with the following:</p>
<ul>
<li><p>Linux Fundamentals</p>
</li>
<li><p>Networking Fundamentals</p>
</li>
<li><p>Basics of application deployment</p>
</li>
</ul>
<p><strong>What is Kubernetes?</strong></p>
<p>Kubernetes, often abbreviated as K8s, is an open-source platform designed to automate deploying, scaling, and operating containerized applications. Initially developed by Google, it’s now maintained by the <a target="_blank" href="https://www.cncf.io/">Cloud Native Computing Foundation</a> (CNCF).</p>
<p>Key highlights of Kubernetes:</p>
<ul>
<li><p><strong>Portable</strong>: Runs on any infrastructure—on-premises, cloud, or hybrid.</p>
</li>
<li><p><strong>Scalable</strong>: Effortlessly handles increased loads by scaling applications up or down.</p>
</li>
<li><p><strong>Self-Healing</strong>: Automatically restarts failed containers, replaces unresponsive pods, and ensures desired application states.</p>
</li>
</ul>
<h3 id="heading-broad-kubernetes-architecture"><strong>Broad Kubernetes Architecture</strong></h3>
<p>Kubernetes cluster broadly consists of two planes.</p>
<ul>
<li><p>Control Plane</p>
</li>
<li><p>Data Plane</p>
</li>
</ul>
<p><strong>Control Plane:</strong> Consists of components required to control the cluster</p>
<p><strong>Data Plane:</strong> Consists of components where your data is hosted</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734986240007/d14c1ea5-de12-4359-a71f-b1ea88905fa0.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-core-components-of-kubernetes"><strong>Core Components of Kubernetes</strong></h3>
<p>Understanding the following core components is crucial for a better understanding of Kubernetes:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734987846985/2725c8c8-94f8-4159-b079-588cd05653cc.png" alt class="image--center mx-auto" /></p>
<p><strong>1. Nodes</strong></p>
<p>A Kubernetes cluster consists of nodes, which are physical or virtual machines.</p>
<p><strong>2. Pods</strong></p>
<p>Pods are the smallest deployable units in Kubernetes. Each pod wraps one or more containers (e.g., Docker containers) that share:</p>
<ul>
<li><p>Networking (IP address and ports).</p>
</li>
<li><p>Storage (volumes).</p>
</li>
</ul>
<p><strong>3. Services</strong></p>
<p>Services define how to expose a set of pods to the network. They provide stable endpoints for dynamic pod environments, enabling reliable communication within and outside the cluster.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735014106339/1c8a9b69-d4b6-4754-886a-c28d9639a019.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735014583623/70aaa323-5c3e-414b-b138-91f6ce67bf7e.png" alt class="image--center mx-auto" /></p>
<p>Common service types include:</p>
<ul>
<li><p><strong>ClusterIP</strong>: Internal communication within the cluster.</p>
</li>
<li><p><strong>NodePort</strong>: Exposes the service on each node’s IP at a static port.</p>
</li>
<li><p><strong>LoadBalancer</strong>: Integrates with cloud providers to provide external load balancing.</p>
</li>
</ul>
<p><strong>4. Deployments</strong></p>
<p>Deployments manage the desired state of applications. They ensure a specified number of pods run at any given time and enable rolling updates or rollbacks. Deployments manage replicaset.</p>
<p><strong>5. ConfigMaps and Secrets</strong></p>
<ul>
<li><p><strong>ConfigMaps</strong>: Store non-sensitive configuration data like environment variables.</p>
</li>
<li><p><strong>Secrets</strong>: Securely manage sensitive data such as passwords, tokens, or certificates.</p>
</li>
</ul>
<p><strong>6. Ingress</strong></p>
<p>Ingress manages external access to services within the cluster, typically HTTP or HTTPS. It provides features like URL routing and SSL termination.</p>
<p><strong>7. Namespaces</strong></p>
<p>Namespaces are virtual clusters within a physical cluster, allowing resource isolation for different teams or environments (e.g., dev, test, prod). Think of namespace as a logical grouping of resources. For instance, the namespace “frontend” may group all the pods running various frontend microservices.</p>
<h3 id="heading-how-kubernetes-works"><strong>How Kubernetes Works?</strong></h3>
<p>At its core, Kubernetes follows a declarative model:</p>
<p><strong>Define the desired state</strong>: Use YAML or JSON manifests to specify how applications should run.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735014826544/d7f49269-09ce-4adc-ae47-752853a2d33c.png" alt class="image--center mx-auto" /></p>
<p><strong>Kubernetes reconciles the state</strong>: The control plane ensures the actual state matches the desired state by scheduling pods, replacing failed ones, and scaling workloads.</p>
<p><strong>Key Processes:</strong></p>
<ul>
<li><p><strong>API Server</strong>: Handles requests from users and external systems.</p>
</li>
<li><p><strong>Scheduler</strong>: Assigns workloads to nodes based on resources.</p>
</li>
<li><p><strong>Controller Manager</strong>: Ensures cluster components are functioning correctly.</p>
</li>
<li><p><strong>Kubelet</strong>: Runs on worker nodes to manage pods.</p>
</li>
<li><p><strong>Kube-Proxy</strong>: Manages network rules and communication.</p>
</li>
</ul>
<p><strong>Common Use Cases</strong></p>
<ol>
<li><p><strong>Microservices</strong>: Kubernetes simplifies deploying, scaling, and managing interconnected services.</p>
</li>
<li><p><strong>CI/CD Pipelines</strong>: Automate application builds, tests, and deployments.</p>
</li>
<li><p><strong>Hybrid and Multi-Cloud</strong>: Build resilient applications spanning multiple environments.</p>
</li>
<li><p><strong>Batch Processing</strong>: Manage distributed, high-performance workloads.</p>
</li>
</ol>
<h3 id="heading-conclusion"><strong>Conclusion</strong></h3>
<p>Kubernetes is a game-changer for managing containerized applications at scale. While it has a steep learning curve, mastering its fundamentals sets the stage for building scalable, resilient, and efficient cloud-native applications. Whether you’re a developer or a DevOps engineer, understanding Kubernetes basics is an essential step in modern cloud computing.</p>
]]></content:encoded></item><item><title><![CDATA[Deploy static resume on an Apache Server]]></title><description><![CDATA[Apache Web server is a free open server that serves web requests over HTTP. It is one of the widely used web servers in the internet and is known for being secure and reliable.
In this blog, we will deploy this server in Ubuntu and deploy a static si...]]></description><link>https://blog.ceruleancloud.ca/deploy-static-resume-on-an-apache-server</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/deploy-static-resume-on-an-apache-server</guid><category><![CDATA[cloud-basics]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Tue, 17 Dec 2024 12:57:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742530858172/7822a585-a9c8-4ad9-a6b9-87cff059983c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://httpd.apache.org/">Apache Web server</a> is a free open server that serves web requests over HTTP. It is one of the widely used web servers in the internet and is known for being secure and reliable.</p>
<p>In this blog, we will deploy this server in Ubuntu and deploy a static site containing a sample resume.</p>
<p><strong><em>Feel free to update it’s contents to make it yours.</em></strong></p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ol>
<li><p>An Ubuntu machine (local or cloud instance). Use this <a target="_blank" href="https://cerulean-cloud-blogs.hashnode.dev/windows-subsystem-for-linux">link</a> to setup WSL if you wish to do so.</p>
</li>
<li><p>Basic knowledge of working on Linux terminal. Use this <a target="_blank" href="https://cerulean-cloud-blogs.hashnode.dev/linux-command-line-interface-cheatsheet">link</a> to familiarize yourself with basic commands.</p>
</li>
</ol>
<h3 id="heading-install-apache-web-server">Install Apache Web Server</h3>
<p>Apache is one of the most popular web servers and is easy to set up on Ubuntu.</p>
<ol>
<li><p><strong>Update package repositories:</strong><br /> Open your terminal and run the following command to ensure all packages are up-to-date:</p>
<pre><code class="lang-plaintext"> sudo apt update
 sudo apt upgrade -y
</code></pre>
</li>
<li><p><strong>Install Apache:</strong><br /> Install Apache using the <code>apt</code> package manager:</p>
<pre><code class="lang-plaintext"> sudo apt install apache2 -y
</code></pre>
</li>
<li><p><strong>Start and enable Apache service:</strong><br /> Ensure the Apache service is running and set to start on boot:</p>
<pre><code class="lang-plaintext"> sudo systemctl start apache2
 sudo systemctl enable apache2
</code></pre>
</li>
<li><p><strong>Verify installation:</strong><br /> Open a browser and visit your server’s IP address (or <a target="_blank" href="http://localhost"><code>http://localhost</code></a> if you're running it locally). You should see the default Apache welcome page.</p>
</li>
</ol>
<h3 id="heading-create-your-static-html-resume">Create Your Static HTML Resume</h3>
<p>Next, we'll create a basic HTML file for your resume.</p>
<ol>
<li><p><strong>Navigate to the Apache web root directory:</strong><br /> By default, Apache serves files from <code>/var/www/html</code>. Navigate there:</p>
<pre><code class="lang-plaintext"> cd /var/www/html
</code></pre>
</li>
<li><p><strong>Backup the default index file:</strong></p>
<pre><code class="lang-plaintext"> sudo mv index.html index.html.bak
</code></pre>
</li>
<li><p><strong>Create a new</strong> <code>index.html</code> file:<br /> Use a text editor like <code>nano</code> to create your resume file:</p>
<pre><code class="lang-plaintext"> sudo nano index.html
</code></pre>
</li>
<li><p><strong>Add basic HTML content for your resume:</strong><br /> Paste the following example content:</p>
<pre><code class="lang-xml"> htmlCopy code<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>My Resume<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
         <span class="hljs-selector-tag">body</span> {
             <span class="hljs-attribute">font-family</span>: Arial, sans-serif;
             <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span>;
             <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.6</span>;
         }
         <span class="hljs-selector-tag">h1</span> {
             <span class="hljs-attribute">text-align</span>: center;
         }
         <span class="hljs-selector-class">.section</span> {
             <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">20px</span>;
         }
     </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>John Doe<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Email: john.doe@example.com | Phone: (123) 456-7890<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">hr</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section"</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Professional Summary<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Motivated and detail-oriented professional with experience in web development, system administration, and cloud technologies. Passionate about delivering impactful solutions and learning new technologies.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
     <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section"</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Experience<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Software Engineer, ABC Corp<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Jan 2020 - Present<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
             <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Developed and maintained scalable web applications using modern frameworks.<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
             <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Implemented CI/CD pipelines to automate deployments.<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
         <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
     <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section"</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Education<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Bachelor of Science in Computer Science<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>XYZ University, 2019<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
     <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
</li>
<li><p><strong>Save and exit:</strong><br /> Press <code>Ctrl+O</code> to save and <code>Ctrl+X</code> to exit the editor.</p>
</li>
</ol>
<p><strong><em>PS: Alternatively, if you are using WSL, you can use a notepad to create index.html file and simply copy paste it to your Linux directory using Windows File Explorer.</em></strong></p>
<h3 id="heading-test-your-website">Test Your Website</h3>
<ol>
<li><p><strong>Set correct permissions:</strong><br /> Ensure the file has the right permissions:</p>
<pre><code class="lang-plaintext"> sudo chmod 644 /var/www/html/index.html
</code></pre>
</li>
<li><p><strong>Access your website:</strong><br /> Open a browser and navigate to your server’s IP or domain name (e.g. <code>http://127.0.0.1</code>). You should see your resume displayed.</p>
</li>
</ol>
<p>In just a few steps, you’ve set up an Apache web server and hosted your static HTML resume. This is a great starting point for understanding how web-apps are hosted in real life.</p>
<p>Cheers!</p>
]]></content:encoded></item><item><title><![CDATA[Firewalls and Firewalld in Ubuntu]]></title><description><![CDATA[In today's interconnected world, protecting your systems from unauthorized access is non-negotiable. This is where firewalls come in, serving as your first line of defense against potential cyber threats. If you're using Ubuntu and want to efficientl...]]></description><link>https://blog.ceruleancloud.ca/firewalls-and-firewalld-in-ubuntu</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/firewalls-and-firewalld-in-ubuntu</guid><category><![CDATA[firewall]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Sun, 15 Dec 2024 00:30:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742530958463/3f0ce81a-65bb-4b27-94a4-a008d0781e60.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In today's interconnected world, protecting your systems from unauthorized access is non-negotiable. This is where <strong>firewalls</strong> come in, serving as your first line of defense against potential cyber threats. If you're using Ubuntu and want to efficiently manage your firewall, <strong>Firewalld</strong> is a tool you should know about. In this post, we'll explore what firewalls are, why they matter, and how to use Firewalld to secure your Ubuntu system.</p>
<h3 id="heading-what-is-a-firewall"><strong>What is a Firewall?</strong></h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734221864441/e2496a07-02d8-41b4-9830-47bea4f02d6d.png" alt class="image--center mx-auto" /></p>
<p>A <strong>firewall</strong> is like a security guard for your network. It monitors traffic entering and leaving your system and decides whether to allow or block it based on predefined rules. Firewalls are crucial for:</p>
<ul>
<li><p><strong>Blocking Unwanted Traffic:</strong> Preventing hackers and malicious programs from gaining access.</p>
</li>
<li><p><strong>Allowing Trusted Services:</strong> Ensuring that essential services like SSH or web servers are accessible.</p>
</li>
<li><p><strong>Monitoring and Controlling Traffic:</strong> Keeping tabs on what’s happening in your network.</p>
</li>
</ul>
<p>Think of it as a filter that only lets the right people in while keeping the wrong ones out.</p>
<p>Now, let’s use <strong>Firewalld</strong> - a powerful and flexible firewall management tool for Linux to experiment with firewalls.</p>
<p>Firewalld is a dynamic firewall. It allows you to make real-time changes without restarting the entire service. Here are a few reasons to use Firewalld:</p>
<ul>
<li><p><strong>Zone-based Configuration:</strong> You can define trust levels for network connections using zones.</p>
</li>
<li><p><strong>Real-time Changes:</strong> Add or remove rules without interrupting active connections.</p>
</li>
<li><p><strong>Integration with iptables:</strong> It simplifies the complexity of managing iptables directly.</p>
</li>
</ul>
<h3 id="heading-install-firewalld"><strong>Install Firewalld</strong></h3>
<p>Firewalld isn’t installed by default on Ubuntu, but you can get it up and running in just a few steps:</p>
<pre><code class="lang-plaintext">sudo apt update  
sudo apt install firewalld  
sudo systemctl start firewalld  
sudo systemctl enable firewalld
</code></pre>
<p>These commands update your package lists, install Firewalld, and ensure it starts automatically whenever your system boots.</p>
<h3 id="heading-checking-firewalld-status"><strong>Checking Firewalld Status</strong></h3>
<p>Once installed, verify that Firewalld is active:</p>
<pre><code class="lang-plaintext">sudo systemctl status firewalld
</code></pre>
<p>If it’s running, you’ll see “active (running).”</p>
<h3 id="heading-firewalld-basics"><strong>Firewalld Basics</strong></h3>
<p>Now that you have Firewalld installed, we will quickly run through some basics about configuring it.</p>
<p><strong>Zones in Firewalld</strong></p>
<p>Zones are the core of Firewalld’s functionality. They define trust levels for your network connections. Common zones include:</p>
<ul>
<li><p><strong>Public:</strong> For untrusted networks, like public Wi-Fi.</p>
</li>
<li><p><strong>Home:</strong> For trusted networks, like your private Wi-Fi.</p>
</li>
<li><p><strong>Work:</strong> For office networks with medium trust levels.</p>
</li>
</ul>
<p>You can view active zones with:</p>
<pre><code class="lang-plaintext">firewall-cmd --get-active-zones
</code></pre>
<p><strong>Allowing a Service</strong></p>
<p>Let’s say you’re hosting a website and must allow HTTP traffic. Run:</p>
<pre><code class="lang-plaintext">firewall-cmd --add-service=http --permanent  
firewall-cmd --reload
</code></pre>
<p><strong>Blocking a Service</strong></p>
<p>To block a service like SSH, use:</p>
<pre><code class="lang-plaintext">firewall-cmd --remove-service=ssh --permanent  
firewall-cmd --reload
</code></pre>
<h3 id="heading-practical-example-securing-a-web-server"><strong>Practical Example: Securing a Web Server</strong></h3>
<p>Imagine you’re hosting a web application on an Ubuntu server.</p>
<p><em>PS: Instead of imagining, you can run a simple site inan Apache server and try securing it</em></p>
<p>Here’s how you can secure it with Firewalld:</p>
<ol>
<li><strong>Allow HTTP Traffic:</strong></li>
</ol>
<pre><code class="lang-plaintext">firewall-cmd --add-service=http --permanent  
firewall-cmd --reload
</code></pre>
<ol start="2">
<li><strong>Block Other Traffic:</strong></li>
</ol>
<p>Remove unnecessary services (e.g., SSH):</p>
<pre><code class="lang-plaintext">firewall-cmd --remove-service=ssh --permanent  
firewall-cmd --reload
</code></pre>
<ol start="3">
<li><strong>Verify Rules:</strong></li>
</ol>
<p>Check active rules to ensure only HTTP is allowed:</p>
<pre><code class="lang-plaintext">firewall-cmd --list-all
</code></pre>
<p>This will display the active zone and its rules.</p>
<p>Firewalls are essential for securing your systems. Take a moment today to try and practically implement and play around with this.</p>
]]></content:encoded></item><item><title><![CDATA[Networking Crash Course for the cloud]]></title><description><![CDATA[Networking is the backbone of cloud computing. Whether you're deploying applications, managing virtual networks, or securing data, understanding core networking concepts is essential. In this crash course, we'll explore key networking topics as follo...]]></description><link>https://blog.ceruleancloud.ca/networking-crash-course-for-the-cloud</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/networking-crash-course-for-the-cloud</guid><category><![CDATA[networking]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Thu, 12 Dec 2024 03:16:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742531083123/1c29ca9a-d2d9-414b-8c29-a66f65139bd5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Networking is the backbone of cloud computing. Whether you're deploying applications, managing virtual networks, or securing data, understanding core networking concepts is essential. In this crash course, we'll explore key networking topics as follows:</p>
<ul>
<li><p><strong>OSI Model</strong></p>
</li>
<li><p><strong>TCP/IP Model</strong></p>
</li>
<li><p><strong>What is TCP?</strong></p>
</li>
<li><p><strong>What is UDP?</strong></p>
</li>
<li><p><strong>TCP vs UDP</strong></p>
</li>
<li><p><strong>DNS</strong></p>
</li>
<li><p><strong>SSH</strong></p>
</li>
<li><p><strong>IP Address and Subnetting</strong></p>
</li>
<li><p><strong>IP Classes</strong></p>
</li>
<li><p><strong>CIDR</strong></p>
</li>
</ul>
<h3 id="heading-osi-model"><strong>OSI Model</strong></h3>
<p>The <strong>OSI (Open Systems Interconnection)</strong> model provides a conceptual framework for understanding how data flows through a network. It has <strong>7 layers</strong>, each with specific responsibilities:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733323948043/c4a5774a-555e-4ac5-946d-5a7bc4287223.png" alt class="image--center mx-auto" /></p>
<ol>
<li><p><strong>Physical Layer</strong><br /> Manages the transmission of raw data bits over physical media (e.g., cables, Wi-Fi).<br /> <em>Example</em>: Ethernet cables.</p>
</li>
<li><p><strong>Data Link Layer</strong><br /> Ensures error-free data transfer between adjacent nodes. Includes MAC addresses.<br /> <em>Example</em>: Ethernet, Wi-Fi (802.11).</p>
</li>
<li><p><strong>Network Layer</strong><br /> Handles routing and addressing using IP addresses.<br /> <em>Example</em>: IPv4, IPv6.</p>
</li>
<li><p><strong>Transport Layer</strong><br /> Ensures reliable delivery (TCP) or faster, connectionless transfer (UDP).<br /> <em>Example</em>: TCP, UDP.</p>
</li>
<li><p><strong>Session Layer</strong><br /> Manages sessions and controls connections between applications.<br /> <em>Example</em>: Remote Desktop Protocol (RDP).</p>
</li>
<li><p><strong>Presentation Layer</strong><br /> Translates data formats for applications. Handles encryption and compression.<br /> <em>Example</em>: SSL/TLS encryption.</p>
</li>
<li><p><strong>Application Layer</strong><br /> Interfaces directly with users.<br /> <em>Example</em>: HTTP, FTP, DNS.</p>
</li>
</ol>
<h3 id="heading-tcpip-model"><strong>TCP/IP Model</strong></h3>
<p>The <strong>TCP/IP model</strong> - is essentially a condensed version of the OSI model widely used in modern networking.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733324443958/9681d071-5f10-47e3-a62c-2e17637bbd99.png" alt class="image--center mx-auto" /></p>
<ol>
<li><p><strong>Link Layer</strong><br /> Combines the OSI Physical and Data Link layers.<br /> <em>Example</em>: Ethernet.</p>
</li>
<li><p><strong>Internet Layer</strong><br /> Maps to the OSI Network layer. Handles IP addressing and routing.<br /> <em>Example</em>: IPv4, IPv6.</p>
</li>
<li><p><strong>Transport Layer</strong><br /> Supports reliable (TCP) or best-effort (UDP) delivery of data.<br /> <em>Example</em>: TCP, UDP.</p>
</li>
<li><p><strong>Application Layer</strong><br /> Combines OSI Application, Presentation, and Session layers.<br /> <em>Example</em>: HTTP, FTP.</p>
</li>
</ol>
<h3 id="heading-tcp-vs-udp"><strong>TCP vs. UDP</strong></h3>
<p><strong>What is TCP?</strong></p>
<p>Transmission Control Protocol (TCP) is a communications standard that allows devices and applications to exchange data over a network. It's a fundamental protocol in the Internet Protocol (IP) suite and is a key part of the Internet's rules. TCP is responsible for ensuring that data is delivered <strong>reliably</strong> and in the correct order.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733357992070/2670fe74-b57d-492e-9201-8fe39f22489e.png" alt class="image--center mx-auto" /></p>
<p><strong>TCP (Transmission Control Protocol)</strong></p>
<ul>
<li><p>Ensures reliable data delivery through acknowledgment and retransmission.</p>
</li>
<li><p>Establishes a connection using a three-way handshake.</p>
</li>
<li><p>Suitable for applications needing accuracy (e.g., web browsing, file transfers).</p>
</li>
</ul>
<p><strong>What is UDP?</strong></p>
<p>UDP stands for User Datagram Protocol, a communication protocol used to send data between computers on a network. UDP is often used for time-sensitive applications that require speed over reliability.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733358174949/645add88-b525-42f8-a803-e7c4ec4c88da.png" alt class="image--center mx-auto" /></p>
<p><strong>UDP</strong></p>
<ul>
<li><p>Focuses on speed and low latency.</p>
</li>
<li><p>Does not guarantee delivery, order, or error correction.</p>
</li>
<li><p>Ideal for real-time applications (e.g., video streaming, gaming).</p>
</li>
</ul>
<p><strong>Comparison Table</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Feature</strong></td><td><strong>TCP</strong></td><td><strong>UDP</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Reliability</td><td>Reliable</td><td>Unreliable</td></tr>
<tr>
<td>Speed</td><td>Slower</td><td>Faster</td></tr>
<tr>
<td>Use Cases</td><td>Web, file transfer</td><td>Streaming, gaming</td></tr>
</tbody>
</table>
</div><h3 id="heading-domain-name-system"><strong>Domain Name System</strong></h3>
<p><strong>Domain Name System (DNS)</strong> is the "phonebook" of the internet, translating human-friendly domain names like <a target="_blank" href="http://example.com"><code>example.com</code></a> into IP addresses <code>192.168.4.4</code> . This is required because nodes in a network need to know their “address” - in other words - IP - to reach and communicate.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733957808307/3e6a7637-2060-46dd-ba74-2c403efb11a5.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><strong>Key Components</strong>:</p>
<ul>
<li><p><strong>DNS Servers</strong>: Store mappings of domain names to IPs.</p>
</li>
<li><p><strong>DNS Records</strong>: Types include:</p>
<ul>
<li><p><strong>A Record</strong>: Maps domain to IPv4.</p>
</li>
<li><p><strong>AAAA Record</strong>: Maps domain to IPv6.</p>
</li>
<li><p><strong>CNAME</strong>: Points to another domain.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="heading-ssh">SSH</h3>
<p>Secure SHell is a network protocol that allows users to securely access and manage remote computers and systems over an unsecured network. It is commonly used in Linux environments.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733958381846/51aca3d0-1c00-40aa-84ae-8ff2fb96b1d1.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-ip-addressing-and-subnetting"><strong>IP Addressing and Subnetting</strong></h3>
<h3 id="heading-ip-addressing"><strong>IP Addressing</strong></h3>
<p>An <strong>IP address</strong> is a unique identifier for devices on a network. It comes in two versions:</p>
<ul>
<li><p><strong>IPv4</strong>: 32-bit (e.g., <code>192.168.1.1</code>).</p>
</li>
<li><p><strong>IPv6</strong>: 128-bit (e.g., <code>2001:0db8::1</code>).</p>
</li>
</ul>
<h3 id="heading-subnetting"><strong>Subnetting</strong></h3>
<p><strong>Subnetting</strong> divides a network into smaller subnetworks, improving efficiency and security.</p>
<ul>
<li><p><strong>Subnet Mask</strong>: Determines the network and host portions of an IP.<br />  <em>Example</em>:</p>
<ul>
<li><p>IP: <code>192.168.1.1</code></p>
</li>
<li><p>Subnet Mask: <code>255.255.255.0</code></p>
</li>
<li><p>Network: <code>192.168.1.0</code></p>
</li>
<li><p>Hosts: <code>192.168.1.1</code> to <code>192.168.1.254</code></p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-ip-classes"><strong>IP Classes</strong></h3>
<p>IP addresses are divided into <strong>classes</strong> to categorize networks based on their size and usage. IP classes were part of the original design of IPv4 and are used to define ranges of IP addresses. Below are the key classes:</p>
<p><strong>Class A</strong></p>
<ul>
<li><p><strong>Range:</strong> <code>1.0.0.0</code> to <code>126.255.255.255</code></p>
</li>
<li><p><strong>Default Subnet Mask:</strong> <code>255.0.0.0</code> (or <code>/8</code>)</p>
</li>
<li><p><strong>Purpose:</strong> Very large networks, typically used by organizations with a huge number of devices.</p>
</li>
<li><p><strong>Addressing:</strong></p>
<ul>
<li><p>The <strong>first octet</strong> represents the network.</p>
</li>
<li><p>The remaining three octets represent the host.</p>
</li>
</ul>
</li>
</ul>
<p>Example: <code>10.0.0.1</code><br />Network: <code>10.0.0.0</code><br />Hosts: Over 16 million addresses.</p>
<p><strong>Class B</strong></p>
<ul>
<li><p><strong>Range:</strong> <code>128.0.0.0</code> to <code>191.255.255.255</code></p>
</li>
<li><p><strong>Default Subnet Mask:</strong> <code>255.255.0.0</code> (or <code>/16</code>)</p>
</li>
<li><p><strong>Purpose:</strong> Medium-sized networks, such as universities or large businesses.</p>
</li>
<li><p><strong>Addressing:</strong></p>
<ul>
<li><p>The <strong>first two octets</strong> represent the network.</p>
</li>
<li><p>The last two octets represent the host.</p>
</li>
</ul>
</li>
</ul>
<p>Example: <code>172.16.0.1</code><br />Network: <code>172.16.0.0</code><br />Hosts: About 65,000 addresses.</p>
<p><strong>Class C</strong></p>
<ul>
<li><p><strong>Range:</strong> <code>192.0.0.0</code> to <code>223.255.255.255</code></p>
</li>
<li><p><strong>Default Subnet Mask:</strong> <code>255.255.255.0</code> (or <code>/24</code>)</p>
</li>
<li><p><strong>Purpose:</strong> Small networks, such as small businesses.</p>
</li>
<li><p><strong>Addressing:</strong></p>
<ul>
<li><p>The <strong>first three octets</strong> represent the network.</p>
</li>
<li><p>The last octet represents the host.</p>
</li>
</ul>
</li>
</ul>
<p>Example: <code>192.168.1.1</code><br />Network: <code>192.168.1.0</code><br />Hosts: Up to 254 addresses.</p>
<p><strong>Class D</strong></p>
<ul>
<li><p><strong>Range:</strong> <code>224.0.0.0</code> to <code>239.255.255.255</code></p>
</li>
<li><p><strong>Purpose:</strong> Reserved for <strong>multicasting</strong> (sending data to multiple hosts simultaneously).</p>
</li>
<li><p><strong>Addressing:</strong> Does not use subnetting.</p>
</li>
</ul>
<p><strong>Class E</strong></p>
<ul>
<li><p><strong>Range:</strong> <code>240.0.0.0</code> to <code>255.255.255.255</code></p>
</li>
<li><p><strong>Purpose:</strong> Reserved for <strong>experimental</strong> purposes. Not used for general networking.</p>
</li>
</ul>
<p><strong>Special Ranges</strong></p>
<ol>
<li><p><strong>Private IP Addresses:</strong></p>
<ul>
<li><p>Reserved for internal use within a network.</p>
</li>
<li><p>Class A: <code>10.0.0.0</code> to <code>10.255.255.255</code></p>
</li>
<li><p>Class B: <code>172.16.0.0</code> to <code>172.31.255.255</code></p>
</li>
<li><p>Class C: <code>192.168.0.0</code> to <code>192.168.255.255</code></p>
</li>
</ul>
</li>
<li><p><strong>Loopback Address:</strong> <code>127.0.0.0</code> to <code>127.255.255.255</code> (used for testing and diagnostics).</p>
</li>
<li><p><strong>APIPA:</strong> <code>169.254.0.0</code> to <code>169.254.255.255</code> (used for automatic addressing when DHCP fails).</p>
</li>
</ol>
<p><strong>Classes are less relevant today because of the following reasons:</strong></p>
<ul>
<li><p><strong>Classless Inter-Domain Routing (CIDR):</strong> Modern IP address allocation uses CIDR, which allows flexible subnetting regardless of class.</p>
</li>
<li><p><strong>IPv6:</strong> The introduction of IPv6 reduces reliance on IPv4 classes.</p>
</li>
</ul>
<h3 id="heading-cidr-efficient-ip-allocation"><strong>CIDR: Efficient IP Allocation</strong></h3>
<p><strong>CIDR (Classless Inter-Domain Routing)</strong> simplifies IP allocation using a <strong>prefix notation</strong> (e.g., <code>/24</code>).</p>
<ul>
<li><p><strong>Example</strong>: <code>192.168.1.0/24</code></p>
<ul>
<li><p><code>/24</code> means the first 24 bits define the network.</p>
</li>
<li><p>Host range: <code>192.168.1.1</code> to <code>192.168.1.254</code>.</p>
</li>
</ul>
</li>
</ul>
<p><strong>Why It Matters in the Cloud</strong><br />CIDR is widely used in defining <strong>virtual private networks (VPNs)</strong>, configuring <strong>VPCs</strong> in AWS, or <strong>VNets</strong> in Azure.</p>
<p>This concludes our networking crash course for the cloud.</p>
]]></content:encoded></item><item><title><![CDATA[Build your first Docker Container]]></title><description><![CDATA[In this blog, let’s dive into building your first Docker image. We aim to create an Ubuntu-based image with Python 3 installed in it.
Why Build Your Docker Image?
Docker images are the foundation of containers. While public images on Docker Hub are c...]]></description><link>https://blog.ceruleancloud.ca/build-your-first-docker-container</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/build-your-first-docker-container</guid><category><![CDATA[containers]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Fri, 29 Nov 2024 15:01:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742531191807/860d500f-d45e-4d4f-ae19-912e87b14143.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this blog, let’s dive into building your first Docker image. We aim to create an Ubuntu-based image with Python 3 installed in it.</p>
<h2 id="heading-why-build-your-docker-image">Why Build Your Docker Image?</h2>
<p>Docker images are the foundation of containers. While public images on Docker Hub are convenient, building your image gives you control over the environment. This ensures consistency across your deployments and provides a deeper understanding of Docker's work.</p>
<p>Ready? Let’s get started!</p>
<p><strong>Set Up Docker</strong></p>
<p>First, make sure Docker is installed and running on your machine.</p>
<p>Follow this blog to install Docker on your Linux/WSL.</p>
<p>Verify installation by running:</p>
<pre><code class="lang-plaintext">docker --version
</code></pre>
<p><strong>Create a</strong> <code>Dockerfile</code></p>
<p>The <code>Dockerfile</code> is a simple text file with instructions to build your Docker image. Create a project directory to keep things tidy:</p>
<pre><code class="lang-plaintext">mkdir docker-python &amp;&amp; cd docker-python
</code></pre>
<p>Now, create a file named <code>Dockerfile</code>:</p>
<p>PS: The file is Dockerfile with <strong>no extension</strong></p>
<pre><code class="lang-plaintext">touch Dockerfile
</code></pre>
<p><strong>Write the Dockerfile</strong></p>
<p>Here’s what your <code>Dockerfile</code> should look like:</p>
<pre><code class="lang-plaintext">#Ubuntu as the base image  
FROM ubuntu:latest  

# Update and install Python 3  
RUN apt-get update &amp;&amp; apt-get install -y python3
</code></pre>
<p><strong>Let’s examine the above file</strong></p>
<ol>
<li><p><code>FROM ubuntu:latest</code>: Starts with the latest Ubuntu base image.</p>
</li>
<li><p><code>RUN</code>: Updates the package list and installs Python 3.</p>
</li>
</ol>
<p><strong>Build the Image</strong></p>
<p>Time to turn the <code>Dockerfile</code> into a Docker image. Run:</p>
<pre><code class="lang-plaintext">docker build -t ubuntu-python3 .
</code></pre>
<p><strong>Command Explanation:</strong></p>
<ul>
<li><p>The <code>-t ubuntu-python3</code> flag tags the image with a name. In our case it’s <code>ubuntu-python3</code></p>
</li>
<li><p>The <code>.</code> specifies the build context (current directory).</p>
</li>
</ul>
<p>If everything goes well, Docker will download the Ubuntu base image and install Python 3.</p>
<p><strong>Verify Your Image</strong></p>
<p>Check if your image is built:</p>
<pre><code class="lang-plaintext">docker images
</code></pre>
<p>You should see <code>ubuntu-python3</code> in the list.</p>
<p><strong>Run a Container</strong></p>
<p>Let’s test our image by running a container:</p>
<pre><code class="lang-plaintext">docker run ubuntu-python3
</code></pre>
<p>If everything goes well, your container should be running now.</p>
<p>Run the following command to confirm.</p>
<pre><code class="lang-plaintext">docker ps -a
</code></pre>
<p>Run the following command to connect to the docker container you just created</p>
<pre><code class="lang-plaintext">docker exec -it ubuntu-python3 /bin/bash
</code></pre>
<p>You should now see a “new terminal”. This is the terminal of your container.</p>
<p>Run <code>python -V</code> In the terminal, you will see the Python version.</p>
<p>If everything went well, you just built your first Docker image, ran it as a container and established a session with the container to run a command.</p>
]]></content:encoded></item><item><title><![CDATA[Docker - Hands-on]]></title><description><![CDATA[Before going ahead with installing and playing around with Docker, let’s have a quick recap of the significant differences between Virtual Machines and Containers.
Virtual Machines
Virtual Machines emulate an entire computer, including its own operat...]]></description><link>https://blog.ceruleancloud.ca/docker-hands-on</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/docker-hands-on</guid><category><![CDATA[containers]]></category><category><![CDATA[cloud-basics]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Sun, 24 Nov 2024 18:00:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742531282324/448b5b8b-3fb7-4617-949b-2e2e0fe8bda2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Before going ahead with installing and playing around with Docker, let’s have a quick recap of the significant differences between Virtual Machines and Containers.</p>
<p><strong>Virtual Machines</strong></p>
<p>Virtual Machines emulate an entire computer, including its own operating system, on top of a physical server. They run on a hypervisor, which allows multiple VMs to operate on the same physical machine.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732470295471/1eb036e6-a43d-4580-80d3-34f476ea6a33.png" alt class="image--center mx-auto" /></p>
<p><strong>Containers</strong></p>
<p>Containers are lightweight, standalone units of software that include everything an application needs to run: code, libraries, dependencies, and configuration files. They share the host operating system's kernel, making them highly efficient and fast to start.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732470400938/ca3e2d0c-3443-4343-b020-a0e9b5f74064.png" alt class="image--center mx-auto" /></p>
<p><strong>Docker</strong></p>
<p>Docker is one of the most popular container runtimes, making it simple to create, deploy, and run applications in containers.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732470573042/dcdee26c-7a50-4c22-8d8f-acd62cc0bcec.png" alt class="image--center mx-auto" /></p>
<p>Here’s a step-by-step guide to install <strong>Docker on Ubuntu</strong> and test it with a container.</p>
<p>Ensure your system is up-to-date before installing Docker.</p>
<pre><code class="lang-plaintext">sudo apt update
sudo apt upgrade -y
</code></pre>
<p>Docker requires certain prerequisites. Install them using the following commands:</p>
<pre><code class="lang-plaintext">sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
</code></pre>
<p>Add the Docker GPG key to verify its packages:</p>
<pre><code class="lang-plaintext">curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
</code></pre>
<p>Add the Docker repository to your system:</p>
<pre><code class="lang-plaintext">echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null
</code></pre>
<p>Update the APT package index and install Docker:4</p>
<pre><code class="lang-plaintext">sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io
</code></pre>
<p>Ensure Docker is installed correctly:</p>
<pre><code class="lang-plaintext">sudo docker --version
</code></pre>
<p>Run the <code>hello-world</code> container to test your Docker installation:</p>
<pre><code class="lang-plaintext">sudo docker run hello-world
</code></pre>
<p>To run Docker without <code>sudo</code>, add your user to the Docker group:</p>
<pre><code class="lang-plaintext">sudo usermod -aG docker $USER
</code></pre>
<p>Then, log out and back in for the changes to take effect.</p>
<p>Understanding the distinction between containers and virtual machines can help you choose the right technology. While containers are ideal for lightweight, cloud-native applications, virtual machines provide robust isolation and flexibility for diverse workloads.</p>
]]></content:encoded></item><item><title><![CDATA[What is a Virtual Machine Monitor?]]></title><description><![CDATA[Imagine you’re running a small business with limited hardware resources. You’d like to maximize these resources but can’t afford additional servers. Here’s where virtualization, specifically hypervisors, comes in.
Hypervisors allow you to run multipl...]]></description><link>https://blog.ceruleancloud.ca/what-is-a-virtual-machine-monitor</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/what-is-a-virtual-machine-monitor</guid><category><![CDATA[cloud-basics]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Wed, 13 Nov 2024 12:58:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742531449134/48a1f3fc-f366-4dbd-a812-4e1e6a0c481b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine you’re running a small business with limited hardware resources. You’d like to maximize these resources but can’t afford additional servers. Here’s where virtualization, specifically hypervisors, comes in.</p>
<p>Hypervisors allow you to run multiple “virtual” machines on a single physical machine, each with its own operating system and applications. By doing this, you make the most out of your hardware and reduce costs significantly. So, let’s dive into what hypervisors are, how they work, and highlight some popular options.</p>
<p><strong>What is a Hypervisor?</strong></p>
<p>A hypervisor, also known as a <strong>virtual machine monitor</strong> (VMM), is software that sits between the hardware and the virtual machines (VMs). It creates, runs, and manages these VMs by sharing the physical resources like CPU, memory, and storage with them. Think of a hypervisor as a “conductor” of resources, ensuring that each VM gets what it needs while sharing the same hardware infrastructure.</p>
<p>There are two main types of hypervisors:</p>
<ol>
<li><p><strong>Type 1 Hypervisors (Bare-Metal Hypervisors)</strong>: These run directly on the host hardware, without a host operating system. They’re efficient and offer high performance since they interact directly with the physical hardware. Examples include <a target="_blank" href="https://www.vmware.com/products/cloud-infrastructure/esxi-and-esx">VMware ESXi</a> and <a target="_blank" href="https://learn.microsoft.com/en-us/windows-server/virtualization/hyper-v/hyper-v-overview">Microsoft Hyper-V</a>.</p>
</li>
<li><p><strong>Type 2 Hypervisors (Hosted Hypervisors)</strong>: These run on top of a host operating system like Windows or Linux. They are easier to set up but slightly less efficient because they depend on the host OS. Examples include <a target="_blank" href="https://www.vmware.com/products/desktop-hypervisor/workstation-and-fusion">VMware Workstation</a> and <a target="_blank" href="https://www.virtualbox.org/">Oracle VirtualBox</a>.</p>
</li>
</ol>
<p>Type 1 hypervisors are often preferred for large-scale enterprise environments, while Type 2 hypervisors are popular for smaller, testing-oriented setups.</p>
<p>Let’s talk about some of the about said hypervisors in detail here:</p>
<p><strong>VMware ESXi</strong></p>
<p>VMware ESXi is a leading <strong>Type 1 hypervisor</strong> known for its reliability and performance. Part of VMware’s larger vSphere suite, ESXi allows businesses to run multiple VMs on a single host, manage resources efficiently, and create highly available virtual environments.</p>
<p><strong>Key Features</strong>:</p>
<ul>
<li><p><strong>Bare-Metal Architecture</strong>: Directly installed on server hardware for top performance.</p>
</li>
<li><p><strong>Efficient Resource Management</strong>: Allows fine-tuning of CPU, memory, and storage allocation.</p>
</li>
<li><p><strong>vMotion</strong>: A well-known feature in VMware’s suite, enabling live migration of VMs between hosts with no downtime.</p>
</li>
<li><p><strong>Snapshot Support</strong>: Lets you take snapshots of your VMs, which is useful for backups and rollbacks during updates or testing.</p>
</li>
</ul>
<p>VMware ESXi is a powerful choice for enterprises looking to virtualize their servers, but it can be more costly compared to other hypervisors.</p>
<p><strong>Microsoft Hyper-V</strong></p>
<p>Microsoft’s Hyper-V is another popular <strong>Type 1 hypervisor</strong>, integrated into Windows Server and available on some Windows desktop editions. Hyper-V is highly compatible with Microsoft products, making it a go-to choice for Windows-centric environments.</p>
<p><strong>Key Features</strong>:</p>
<ul>
<li><p><strong>Native Windows Integration</strong>: Works seamlessly with Windows-based systems and other Microsoft products like System Center.</p>
</li>
<li><p><strong>Dynamic Memory</strong>: Allocates memory dynamically to VMs based on their demand, helping to optimize usage.</p>
</li>
<li><p><strong>Replica Support</strong>: Offers built-in disaster recovery by replicating VMs to other Hyper-V hosts.</p>
</li>
</ul>
<p>Hyper-V is often chosen by businesses already invested in the Microsoft ecosystem, thanks to its integration with Windows and cost-effectiveness.</p>
<p><strong>Oracle VM VirtualBox</strong></p>
<p>For those seeking a free and open-source option, <strong>Oracle VirtualBox</strong> is a <strong>Type 2 hypervisor</strong> that runs on various platforms, including Windows, macOS, and Linux. Though not ideal for enterprise-scale deployments, it’s popular among developers and for testing purposes.</p>
<p><strong>Key Features</strong>:</p>
<ul>
<li><p><strong>Cross-Platform Support</strong>: Runs on multiple host operating systems, making it flexible for development needs.</p>
</li>
<li><p><strong>Snapshot Capability</strong>: Allows you to save VM states, making it easy to revert if needed.</p>
</li>
<li><p><strong>Extensive Hardware Support</strong>: Supports a wide range of guest OS and hardware configurations, though it may lack the performance of Type 1 hypervisors.</p>
</li>
</ul>
<p>While VirtualBox may not be as performant or secure as Type 1 hypervisors, its versatility and cost make it ideal for smaller projects and educational use.</p>
<p><strong>KVM (Kernel-Based Virtual Machine)</strong></p>
<p>KVM is a Linux-based open-source hypervisor, often used in data centers and for Linux virtual environments. It turns the Linux kernel into a Type 1 hypervisor, offering a high-performance solution.</p>
<p><strong>Key Features</strong>:</p>
<ul>
<li><p><strong>Linux Integration</strong>: Built into the Linux kernel, so it’s a natural fit for Linux-based systems.</p>
</li>
<li><p><strong>Open-Source</strong>: Free to use and supported by a strong community.</p>
</li>
<li><p><strong>Scalability and Performance</strong>: Suitable for data centers and cloud providers, powering environments like Google Cloud.</p>
</li>
</ul>
<p>KVM has gained popularity in cloud and enterprise environments because of its scalability and open-source flexibility.</p>
<p>To close, choosing a hypervisor depends on your specific needs. For high-performance and enterprise-grade solutions, <strong>VMware ESXi</strong> and <strong>Hyper-V</strong> are excellent choices. If you’re looking for something budget-friendly and versatile for testing, <strong>VirtualBox</strong> is a solid option, while <strong>KVM</strong> shines in Linux environments and for cloud solutions.</p>
<p>Hypervisors have transformed IT infrastructure, enabling companies to do more with less by running multiple virtual environments on a single piece of hardware. Whether you’re running a test lab at home or managing an enterprise environment, understanding and using hypervisors effectively is a vital skill in today’s tech landscape.</p>
]]></content:encoded></item><item><title><![CDATA[Linux Command Line Interface Cheatsheet]]></title><description><![CDATA[Linux is an open-source operating system known for its versatility, stability, and efficiency. It is widely used in various fields, from personal computing to servers, development, and embedded systems. Linux is built on a modular design, allowing us...]]></description><link>https://blog.ceruleancloud.ca/linux-command-line-interface-cheatsheet</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/linux-command-line-interface-cheatsheet</guid><category><![CDATA[cloud-basics]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Sun, 10 Nov 2024 18:23:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742531494958/3d7d5f09-6eeb-4010-a5ac-fb29bc8f8fd7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Linux</strong> is an open-source operating system known for its versatility, stability, and efficiency. It is widely used in various fields, from personal computing to servers, development, and embedded systems. Linux is built on a modular design, allowing users to modify and customize it according to their needs. One of the key reasons for its popularity is the powerful command-line interface (CLI), which provides deep control over the system through various commands.</p>
<p>The <strong>CLI</strong> is a text-based interface that allows users to interact with the operating system by typing commands. Unlike graphical user interfaces (GUIs), the CLI may initially seem intimidating, but it offers unmatched flexibility, precision, and speed once mastered. For system administrators, developers, and power users, the CLI is essential for managing files, running scripts, troubleshooting, and automating tasks.</p>
<p>For those new to Linux, becoming familiar with basic commands is the first step to mastering the CLI. The table below introduces essential commands in a logical sequence, covering navigation, file management, system monitoring, networking, and basic administrative tasks. These commands will provide a solid foundation for beginners and help them perform routine tasks and manage their system effectively.</p>
<p><strong>Linux Basic Commands</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong><em>Command</em></strong></td><td><strong><em>Description</em></strong></td><td><strong><em>Example Usage</em></strong></td></tr>
</thead>
<tbody>
<tr>
<td><code>pwd</code></td><td>Prints the current working directory.</td><td><code>pwd</code></td></tr>
<tr>
<td><code>ls</code></td><td>Lists files and directories in the current directory.</td><td><code>ls -l</code></td></tr>
<tr>
<td><code>cd</code></td><td>Changes the current directory.</td><td><code>cd /home</code></td></tr>
<tr>
<td><code>mkdir</code></td><td>Creates a new directory.</td><td><code>mkdir my_folder</code></td></tr>
<tr>
<td><code>rmdir</code></td><td>Removes an empty directory.</td><td><code>rmdir my_folder</code></td></tr>
<tr>
<td><code>touch</code></td><td>Creates a new empty file.</td><td><code>touch myfile.txt</code></td></tr>
<tr>
<td><code>cp</code></td><td>Copies files or directories.</td><td><code>cp file1.txt file2.txt</code></td></tr>
<tr>
<td><code>mv</code></td><td>Moves or renames files or directories.</td><td><code>mv file1.txt /backup/</code></td></tr>
<tr>
<td><code>rm</code></td><td>Deletes files or directories.</td><td><code>rm file1.txt</code></td></tr>
<tr>
<td><code>cat</code></td><td>Displays the contents of a file.</td><td><code>cat file1.txt</code></td></tr>
<tr>
<td><code>head</code></td><td>Displays the first lines of a file.</td><td><code>head -n 5 file1.txt</code></td></tr>
<tr>
<td><code>tail</code></td><td>Displays the last lines of a file.</td><td><code>tail -n 5 file1.txt</code></td></tr>
<tr>
<td><code>chmod</code></td><td>Changes file or directory permissions.</td><td><code>chmod 755 myfile.txt</code></td></tr>
<tr>
<td><code>chown</code></td><td>Changes file or directory ownership.</td><td><code>chown user:group myfile.txt</code></td></tr>
<tr>
<td><code>find</code></td><td>Searches for files and directories.</td><td><code>find / -name "file1.txt"</code></td></tr>
<tr>
<td><code>grep</code></td><td>Searches for a text pattern within files.</td><td><code>grep "text" file1.txt</code></td></tr>
<tr>
<td><code>ps</code></td><td>Lists current running processes.</td><td><code>ps aux</code></td></tr>
<tr>
<td><code>top</code></td><td>Displays real-time system processes.</td><td><code>top</code></td></tr>
<tr>
<td><code>kill</code></td><td>Terminates a process.</td><td><code>kill 1234</code></td></tr>
<tr>
<td><code>df</code></td><td>Shows disk space usage.</td><td><code>df -h</code></td></tr>
<tr>
<td><code>du</code></td><td>Shows disk usage of files and directories.</td><td><code>du -sh *</code></td></tr>
<tr>
<td><code>free</code></td><td>Displays memory usage.</td><td><code>free -h</code></td></tr>
<tr>
<td><code>uname</code></td><td>Shows system information.</td><td><code>uname -a</code></td></tr>
<tr>
<td><code>ifconfig</code></td><td>Displays network interface configuration.</td><td><code>ifconfig</code></td></tr>
<tr>
<td><code>ping</code></td><td>Tests network connectivity.</td><td><code>ping</code> <a target="_blank" href="http://google.com"><code>google.com</code></a></td></tr>
<tr>
<td><code>wget</code></td><td>Downloads files from the internet.</td><td><code>wget</code> <a target="_blank" href="http://example.com"><code>http://example.com</code></a></td></tr>
<tr>
<td><code>tar</code></td><td>Archives files and directories.</td><td><code>tar -cvf archive.tar my_folder/</code></td></tr>
<tr>
<td><code>nano</code></td><td>Opens a basic text editor.</td><td><code>nano myfile.txt</code></td></tr>
<tr>
<td><code>apt</code></td><td>Installs or updates packages (Debian-based systems).</td><td><code>apt update &amp;&amp; apt install vim</code></td></tr>
<tr>
<td><code>yum</code></td><td>Installs or updates packages (Red Hat-based systems).</td><td><code>yum install nano</code></td></tr>
<tr>
<td><code>reboot</code></td><td>Reboots the system.</td><td><code>reboot</code></td></tr>
<tr>
<td><code>shutdown</code></td><td>Shuts down the system.</td><td><code>shutdown now</code></td></tr>
</tbody>
</table>
</div><p>When you are done testing all the above commands, you should have gathered beginner-level Linux system administration skills.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Linux Filesystem]]></title><description><![CDATA[Before we begin, have a good look at the picture below.

Alright…
The root directory is at the top of the Linux Filesystem hierarchy, represented by a forward slash (/). This is the starting point for all paths in the Linux file system.
The next leve...]]></description><link>https://blog.ceruleancloud.ca/understanding-linux-filesystem</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/understanding-linux-filesystem</guid><category><![CDATA[cloud-basics]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Thu, 07 Nov 2024 21:45:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742749210593/47fee62b-1d78-4254-a652-37458fb05bf2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Before we begin, have a good look at the picture below.</p>
<p><img src="https://made2ubyvi.wordpress.com/wp-content/uploads/2023/05/filesystem.png?w=950" alt /></p>
<p>Alright…</p>
<p>The root directory is at the top of the Linux Filesystem hierarchy, represented by a <strong>forward slash (/)</strong>. This is the starting point for all paths in the Linux file system.</p>
<p>The next level down is where the system and configuration files are stored, usually in the <strong>/etc</strong> directory. This includes important files like configuration files for system services and startup scripts.</p>
<p>The <strong>/bin</strong> and <strong>/sbin</strong> directories contain the essential binary files for basic system operations. /bin contains executables that are required for the system to run, while <strong>/sbin</strong> contains executables that are used by the system administrator for system maintenance tasks.</p>
<p>When you install services on a Linux server, let's say, for instance, <a target="_blank" href="https://httpd.apache.org/">Apache Web Server</a>, the config files would most probably located under <strong>/etc</strong> since that is where Linux, by default, adds the config files.</p>
<p>The <strong>/usr</strong> directory contains user-level binaries, libraries, and documentation for installed software. This includes applications installed through package managers like apt, as well as other utilities and libraries.</p>
<p>The <strong>/var</strong> directory contains variable data files, such as log files, spool files, and temporary files. You may find the log files of the server that we talked above in <strong>/var</strong> directory.</p>
<p>The <strong>/home</strong> directory is where user home directories are stored. Each user has a subdirectory here, with their files and settings. When you log in, you may be put in your home directory. As a rule of thumb, I begin my new projects from the /home directory for easy navigation and understanding.</p>
<p>Finally, the <strong>/tmp</strong> directory is used for temporary files that are deleted automatically when the system is rebooted.</p>
<p>I hope this gives you a good overview of the Linux file system hierarchy!</p>
<p>Remember there’s much more to learn as you dive deeper into Linux, but this should give you a solid foundation.</p>
]]></content:encoded></item><item><title><![CDATA[Windows Subsystem for Linux]]></title><description><![CDATA[What is WSL?
Windows Subsystem for Linux is a Windows feature that allows you to run a Linux environment without using a separate virtual machine. WSL leverages Hyper-V - a Microsoft hypervisor to spin up the environment without needing a separate Vi...]]></description><link>https://blog.ceruleancloud.ca/windows-subsystem-for-linux</link><guid isPermaLink="true">https://blog.ceruleancloud.ca/windows-subsystem-for-linux</guid><category><![CDATA[cloud-basics]]></category><dc:creator><![CDATA[Cerulean Cloud]]></dc:creator><pubDate>Wed, 06 Nov 2024 16:03:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742749413397/d262d9e2-3ae4-4467-8e5f-85249348902c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>What is WSL?</strong></p>
<p>Windows Subsystem for Linux is a Windows feature that allows you to run a Linux environment without using a separate virtual machine. WSL leverages Hyper-V - a Microsoft hypervisor to spin up the environment without needing a separate Virtual Machine.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730826801388/220225b5-cb92-44c4-8729-9ac4f0c4b865.png" alt class="image--center mx-auto" /></p>
<p><strong>What is the need for WSL?</strong></p>
<p>Windows has an amazing UI but a bad development environment. Linux, on the other hand, has some good development tools. Especially in Cloud and DevOps, where “automating everything” is part of the role, the ability to write scripts without switching between two machines can enhance productivity.</p>
<p>Installing various Windows Command Prompt modules and tools has always been a pain for me. Linux is straightforward. That is also one major reason why I prefer working on Linux terminals.</p>
<p><strong>Installing Windows Subsystem for Linux</strong></p>
<ul>
<li><p><strong>Open PowerShell as Administrator</strong>: Search for PowerShell in the Start Menu, right-click it, and select <strong>Run as Administrator</strong>.</p>
</li>
<li><p><strong>Run the WSL Installation Command</strong>: Enter the following command in PowerShell to enable WSL and install the default Linux distribution (usually Ubuntu):</p>
</li>
<li><pre><code class="lang-plaintext">      wsl --install
</code></pre>
</li>
<li><p><strong>Run the WSL Installation Command</strong>: Enter the following command in PowerShell to enable WSL and install the default Linux distribution (usually Ubuntu):</p>
</li>
<li><p><strong>Launch Your Linux Distro</strong>: After the restart, open your Start Menu and search for your newly installed Linux distribution (e.g., <strong>Ubuntu</strong>). Click to launch it.</p>
</li>
<li><p><strong>Complete Initial Setup</strong>: When you open the Linux distribution for the first time, it may take a few moments to complete its setup. You'll be prompted to create a username and password.</p>
</li>
<li><p><strong>Verify the Installation</strong>: Once logged in, check that everything is working by running a simple command, like:</p>
</li>
<li><pre><code class="lang-plaintext">    uname -a
</code></pre>
<p>  <strong>Check if Your System Supports WSL 2</strong>: WSL 2 requires Windows 10 Version 1903 or higher with Build 18362 or higher.</p>
</li>
<li><p><strong>Install the Virtual Machine Platform</strong>: Open PowerShell as Administrator again and run:</p>
<pre><code class="lang-plaintext">  powershellCopy codedism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
</code></pre>
</li>
<li><p><strong>Set WSL 2 as the Default Version</strong>:</p>
<pre><code class="lang-plaintext">  powershellCopy codewsl --set-default-version 2
</code></pre>
</li>
</ul>
<p>If you want to try other distributions, you can do so by opening the Microsoft Store, searching for <strong>Linux</strong> or <strong>WSL</strong>, and choosing additional distributions to install.</p>
]]></content:encoded></item></channel></rss>