Showing posts with label DevOps. Show all posts
Showing posts with label DevOps. Show all posts

Saturday, 25 July 2026

Massive Kubernetes: Comparing EKS, OKE, GKE, and AKS on Scale, Costs, and Reality

Kubernetes was never really built with a hard cap on cluster size, but every major cloud provider puts one on it anyway. Why? Because the underlying engine (etcd, the API server, and the scheduler) can only track and process so much data before things start falling apart.

Over the last couple of years, the big four managed services—AWS (EKS), Oracle (OKE), Google (GKE), and Microsoft (AKS)—have been pushing those caps higher and higher. But they aren't doing it for the same reasons, nor are they building it the same way.

Here is where the max worker nodes per cluster stand right now:

Cloud ProviderMax Nodes / ClusterPrevious LimitJump
AWS EKS100,0005,00020x
OCI OKE20,0005,0004x
Google GKE15,0005,0003x
Azure AKS5,000~1,000–5,000Current ceiling
 

What’s driving these massive numbers?
AWS went all-in on AI. AWS raised the EKS ceiling to a massive 100,000 nodes. But this isn't for standard microservices—it's built specifically for giant AI and machine learning workloads. When you're training a massive model across tens of thousands of GPUs, splitting that job across multiple separate clusters is a nightmare. AWS built this so everything can live in one single place.

Oracle is chasing the same crowd. Oracle pushed OKE up to 20,000 nodes. Similar to AWS, they are targeting heavy compute and AI jobs. However, Oracle doesn't just hand you this limit out of the box—you have to meet specific setup requirements to unlock it.

Google takes an engineering approach. GKE supports 15,000 nodes. Google builds its control plane on top of Spanner (their massive distributed database), so they treat this limit less like a marketing stunt and more like an engineering rule based on what their backend can comfortably hold.

Azure is sitting comfortable at 5,000. AKS tops out at 5,000 nodes, which used to be the industry standard limit across the board. Microsoft hasn't rushed to join the mega-cluster war yet, focusing instead on feature stability and node pool limits (where individual pools cap at 1,000 nodes). 

What "Extreme Scale" Actually Takes
Those big headlines hide a lot of fine print. Before you go building a 20,000-node cluster, keep these realities in mind:
1. Oracle hides scale behind an upgrade tier
To cross that 5,000-node mark in OKE, you can’t use the standard free "Basic" tier. You have to move to "OKE Enhanced," run a recent version of Kubernetes, and turn on specific encryption tools. Enhanced clusters give you a proper money-backed uptime guarantee (SLA) along with extra enterprise features, but it means reaching extreme scale requires paying for a higher management tier.

2. AWS built a tool for a specific job
Just because EKS can run 100,000 nodes doesn't mean you should move your company's web apps into one giant cluster. AWS explicitly framed this upgrade around AI training. If you aren't running massive distributed jobs that require raw GPU-to-GPU coordination, a massive cluster just adds risk.

3. Google cares about workload density, not just node count
Google's guidelines are refreshingly honest: node count isn't the only thing that breaks a cluster. If you run thousands of tiny pods that create, crash, and restart constantly, you will overwhelm the API server long before you hit 15,000 physical machines.

4. Azure’s limits depend on how you set it up
With AKS, hitting 5,000 nodes requires using standard load balancers and Virtual Machine Scale Sets. If you fall back to basic networking setups, your limit plummets to 100 nodes. Furthermore, while Azure offers a completely free control plane tier, Microsoft warns against running more than 10 nodes on it for real production workloads.

What Does it Cost?
When it comes to base control plane pricing, all four providers have basically settled on the same price:

  • The baseline cost across the board is $0.10 per cluster, per hour.
  • That comes out to roughly $74 a month just to keep the cluster's control plane alive.

However, the real differences come out in the extra options:
AWS EKS: Offers high-performance control planes for extra-heavy API loads (costing anywhere from $1.65 to over $13 an hour), plus an extended support fee if you refuse to upgrade older Kubernetes versions.

Google GKE: Gives you a ~$74 monthly credit that basically makes your first basic cluster's control plane free. They also offer Autopilot, which changes the game entirely: instead of paying for the servers in your cluster, you only pay for the exact CPU, RAM, and storage your individual pods use.

Azure AKS & Oracle OKE: Both offer totally free control planes on their basic tiers (you only pay for the underlying VMs). But if you want financial uptime guarantees or massive scale, you have to jump to their $0.10/hour paid tiers.

Takeaway on cost: For 99% of teams, the control plane fee is just noise. The real money goes to the compute instances (the actual servers) running your workloads.

Operational Reality: What Actually Breaks First?
If you talk to the engineers who maintain these platforms, they’ll all tell you the same thing: The node cap isn't usually what breaks your cluster.

When clusters fall over at scale, it’s usually because of:
Network exhaustion: Running out of IP addresses in your subnet for pods and services.
Container registry bottlenecks: Trying to pull a 10GB Docker image to 5,000 machines at the exact same second.
API server overload: Thousands of pods constantly talking to the cluster control plane at once.

In fact, the official documentation for almost all of these providers quietly says the same thing: Don't build one giant cluster if you don't have to.
For almost every company, running four or five medium-sized clusters is much safer than running one mega-cluster. It isolates failures, makes updates less terrifying, and lets different teams move at their own pace.

Unless you're training a massive AI model, don't worry too much about who has the biggest number. Focus on SLA tiers, how easy it is to manage multiple small clusters, and how compute costs fit your budget.

Saturday, 18 April 2026

What is Ollama Serve (REST API)

Running LLMs locally is becoming very common, and tools like Ollama make it extremely simple.
But one feature that really unlocks its power is
ollama serve
This turns your local machine into a REST API server for AI models.

When we run:
ollama serve

It starts a local web server. This server allows other applications to talk to your AI models using HTTP requests.

Without serve → You manually run prompts in terminal
With serve → Your apps can call the model like an API

Default API Endpoint: Once the server starts
http://localhost:11434  becomes base URL.

Example API Call
Here’s a  request:
curl http://localhost:11434/api/generate -d '{
  "model": "llama3",
  "prompt": "Explain cloud computing"
}'

Sample API Output (With Metrics):
{
  "model": "llama3",
  "created_at": "2026-04-18T12:10:00Z",
  "response": "Cloud computing is the delivery of computing services over the internet...",
  "done": true,

  "total_duration": 2450000000,
  "load_duration": 800000000,
  "prompt_eval_count": 12,
  "prompt_eval_duration": 200000000,
  "eval_count": 65,
  "eval_duration": 1450000000
}


Now let’s understand this response
Basic Response Fields
model  ===> llama3
created_at  ===>  2026-04-18T12:10:00Z
response    ===>  "Cloud computing is the delivery of computing services over the internet..."
done    ===>  true , Means response is complete, No more data coming

Performance Metrics:
1. total_duration ===>   2450000000 ns → ~2.45 seconds
This is the total time taken ==> From Request received To Final response sent

2. load_duration  ===> 800000000 ns → ~0.8 seconds
Time taken to load the model into memory 
This usually happens On first request ,When model is not already loaded

3. prompt_eval_count  ===>  12 tokens
Number of tokens in your input  

4. prompt_eval_duration   ===>  200000000 ns → ~0.2 seconds

Time model spent reading your question

5. eval_count    ===>  65 tokens

Number of tokens generated in response , This directly affects response size,Cost (in cloud scenarios) and Latency

6. eval_duration    ===>  1450000000 ns → ~1.45 seconds

Time spent generating the response , This is Actual thinking + answering time

Friday, 17 April 2026

Understanding LLM Models: Basics That Help You Choose the Right One

LLMs are everywhere now. Every tool, every platform, every new feature seems to be powered by them.

But when it comes to actually choosing a model, things quickly get confusing.
You start seeing terms like parameters, quantization, context length… and it all feels a bit heavy

This blog will help you understand the key basics in a simple way.

Model Architecture – How the Model Thinks

At a high level, architecture is just how the model is designed to process information.  
Most modern LLMs use something called a Transformer. You don’t need to go deep into it — just know this:
It helps the model understand relationships between words.
Instead of reading text word-by-word like old systems, it looks at the whole sentence and figures out what matters more.
That’s how it understands meaning, tone, and context.

Why should you care?
Because better architecture usually means:
More accurate responses
Better understanding of complex inputs
Smarter outputs overall

Parameters – How Big the Model Is
This is the one you’ll hear the most.

Parameters are basically the size of the model.
More parameters = more “learned knowledge”.

Think of it like this:
Small models are quick and efficient
Large models are more knowledgeable but heavier

But bigger isn’t always better.

Yes, large models can reason better and handle complex tasks.
But they also:
Cost more
Need more compute
Can be slower

So the real question is not “What’s the biggest model?”
It’s “What’s enough for my use case?”

Quantization – Making Models Practical
Quantization is simply a way to make models smaller and faster. Without it, most large language models would be too heavy to run outside of high-end infrastructure.

What “Quantization” Really Means
LLMs normally store weights in high precision like:
FP32 (32-bit float)
FP16 (16-bit float)

Quantization reduces that to:
8-bit (Q8)
6-bit (Q6)
5-bit (Q5)
4-bit (Q4)

So instead of each weight taking 16–32 bits, it might take just 4 bits.
Result:
Much smaller model size
Faster inference
Can run on CPU or smaller GPUs

But:
Slight loss in quality (depends on method)

And honestly, in many real-world cases, that quality drop is barely noticeable. Especially for things like chat, summaries, or general-purpose usage.

You’re basically making a smart trade:
a tiny bit of precision for a huge gain in usability

Where It Gets Slightly Confusing (But Important)
Once you start using quantized models, you’ll see names like:
Q4_0
Q4_1
Q4_K_M
Q4_K_S

At first, it looks like random naming. But there’s actually a simple idea behind it.
Q4 → means 4-bit quantization
The part after _ → tells you how the compression is done
Not All Q4 Are Equal

Older versions like:
Q4_0 → more aggressive, lower quality
Q4_1 → slightly better

Smarter Quantization (The K Family)
Q4_K_M
Q4_K_S

use better techniques (you’ll often see them in tools like llama.cpp).

Instead of compressing everything the same way, they:
Work in small blocks
Apply smarter scaling
Keep important information more intact

Same 4-bit size, but noticeably better quality.
Picking the Right One (Simple Rule)
Q4_K_M → best balance (default choice)
Q4_K_S → slightly faster, slightly less accurate

If you don’t want to overthink it, just go with Q4_K_M.

 
Context Length – How Much It Can Keep in Mind

Context length is like the model’s short-term memory.

It decides how much text the model can look at in one go.
Short context:
Faster
Cheaper
But forgets earlier parts quickly

Long context:
Can handle long documents
Better for conversations and analysis
Slightly more expensive

If your work involves long PDFs, logs, or conversations — this matters a lot.

Embedding Length – How Well It Understands Meaning
This one is less talked about, but very important.

Before a model understands text, it converts words into numbers. These are called embeddings.

Embedding length is just how detailed that representation is.
Higher dimension → richer understanding of meaning

This becomes critical when you're building things like:
Search systems
Recommendations
RAG (retrieval-based AI apps)

If your use case involves “finding similar things” — embeddings matter more than you think.

So, How Do You Choose?

Instead of chasing the biggest or newest model, think in terms of your actual need.

If you need deep reasoning → go for larger models
If you need speed and cost efficiency → smaller + quantized models
If you deal with long inputs → prioritize context length
If you're building search or RAG → focus on embedding quality

It’s always a trade-off. There’s no perfect model.

Tuesday, 20 January 2026

What is the Kube-Scheduler?

kube-scheduler is a watchman. Its primary job is to monitor the API Server for newly created pods that have no nodeName assigned (the "Pending" state). Once it finds one, it evaluates every node in your cluster to find the best possible home based on resources, policies, and hardware constraints.

3-Step Core Workflow
1.Scheduling Queue
Whenever a pod is created, it enters a Pending state and is added to the Scheduling Queue. This isn't a simple FIFO (First-In-First-Out) line; it’s a Priority Queue where pods are sorted based on their PriorityClass. High-priority pods, such as system-critical components, jump to the front of the line to be processed first, while lower-priority pods wait their turn. The scheduler then pulls these pods from the queue one by one to begin the placement process.

2.Filtering:
In this phase, the scheduler runs a series of "Predicates." If a node fails even one of these checks, it is disqualified.
Resource Check (PodFitsResources): Does the node have enough free CPU and Memory to meet the Pod’s requests?
Port Check (PodFitsHostPorts): If a pod requires a specific port on the host (HostPort), is that port already taken by another pod on this node?
Taint/Toleration Check: Nodes can have Taints (repellants). Unless the pod has a matching Toleration, it cannot be scheduled there.
Node Selection: Does the node match the nodeSelector or nodeAffinity labels defined in the Pod spec?

3.Scoring:
After filtering, we might have five nodes that could run the pod. The Scoring phase determines which one should run it. Each node is given a score (usually 0–100) based on several factors:
Least Requested: Favors nodes with more free resources to balance the cluster.
Image Locality: Favors nodes that already have the container image downloaded (speeding up start times).
Affinity/Anti-Affinity: Soft preferences, like "I'd prefer not to be on the same node as other pods from this app for high availability."

The node with the highest score is selected as the "Winner."

Binding: Updating the Cluster State
Once the winner is selected, the scheduler doesn't actually "start" the pod. Instead, it completes a Binding request:
Request to API Server: The scheduler sends a "Binding" object to the kube-apiserver.
API Server Updates etcd: The API Server receives this request, validates it, and updates the Pod's definition in etcd (the cluster's database), setting the nodeName field to the winner's name.
Kubelet Takes Over: The Kubelet (the agent on the worker node) is also watching the API Server. It sees that a pod has been assigned to its node, pulls the image, and starts the container.
    
    

 

Saturday, 17 January 2026

Kubernetes Authorization Modes

In Kubernetes, security is a multi-layered journey. Once a user or service proves their identity—a process known as Authentication—they face a second, more granular challenge: Authorization.

If Authentication asks, "Who are you?", Authorization asks, "What exactly are you allowed to do here?"
In this post, we’ll break down the mechanisms Kubernetes uses to control access and ensure your cluster remains a "Zero Trust" environment.  

1. Node Authorization: 
Node Authorization is a specialized, fixed-purpose authorizer designed specifically for Kubelets. It implements a graph-based check to ensure that a worker node only has access to the resources it absolutely needs to function.

Target: Requests coming from nodes (identified by the system:nodes group and system:node:<nodeName> username).
Technical Logic: It limits a Kubelet's ability to read Secrets, ConfigMaps, and PersistentVolumes. A Kubelet can only access these objects if they are associated with a Pod currently scheduled on that specific node.
Security Impact: This prevents a compromised node from "lateral movement"—it cannot reach out and steal secrets belonging to workloads on other nodes.

2. RBAC: 
Role-Based Access Control (RBAC) is the most common and recommended authorization mechanism. It allows for dynamic, API-driven permission management without requiring an API server restart.

Objects: * Roles/ClusterRoles: Pure sets of permissions (Verbs + Resources + API Groups).
RoleBindings/ClusterRoleBindings: Mapping objects that attach a Subject (User/Group/ServiceAccount) to a Role.
Technical Nuance: RBAC is additive-only. There are no "Deny" rules in RBAC; if no rule grants access, the request is denied by default. It also supports Aggregation, allowing you to combine multiple ClusterRoles into a single "super-role" dynamically.

3. ABAC: Policy-Driven
Attribute-Based Access Control (ABAC) grants access based on a combination of attributes (user, resource, and environment).

Implementation: Unlike RBAC, ABAC policies are defined in a local JSON file on the master node.

Technical Logic: Each line in the policy file is a "Policy Object."For example:
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "alice", "namespace": "dev", "resource": "pods", "readonly": true}}

Downside: ABAC is difficult to manage at scale because any change requires a manual update to the file and a restart of the Kube-API server, making it less agile than RBAC.

4. Webhook Authorization: 
Webhook authorization allows Kubernetes to delegate the "Yes/No" decision to a remote HTTP service. This is the ultimate tool for integrating Kubernetes with enterprise-wide security policies.

Flow: When a request arrives, the API server sends a SubjectAccessReview (a JSON-serialized object) to an external REST endpoint.
 Technical Payload: The payload includes the username, groups, and the specific resource/verb requested. The remote service responds with an allowed: true or false status. 

Use Cases: * Integrating with Open Policy Agent (OPA) for complex logic.

5. AlwaysAllow:
As the name suggests, the AlwaysAllow mode grants every request, regardless of who is asking or what they are trying to do. It completely bypasses all security checks.

Technical Logic: It returns allowed: true for every single API call.
Use Cases: * Local Development: Used in very restricted, single-node local environments (like early-stage minikube setups) where security isn't a concern.
Unit Testing: Used by developers testing API server extensions where they want to isolate the logic from authorization interference.
Risk: Enabling this in a production cluster is a critical security failure. It effectively turns off the cluster's "immune system," allowing any unauthenticated "system:anonymous" user to delete the entire cluster.

6. AlwaysDeny:
The AlwaysDeny mode does exactly the opposite: it rejects every single request.

Technical Logic: It returns allowed: false for everything.
Use Cases: * Security Hardening: It is often used at the very end of a list of authorization modes. If the request doesn't match a Node rule, an RBAC rule, or a Webhook rule, it hits the "final wall" and is rejected.
Emergency Lockdown: In extreme scenarios, an administrator could theoretically set this to prevent any further changes to the cluster state during an active breach investigation.
Nuance: Even with AlwaysDeny, the API server may still allow certain "discovery" endpoints (like /healthz) depending on the version and configuration, but for all intents and purposes, the cluster becomes a "read-only/no-access" vault.
    
        

Saturday, 10 January 2026

Kubernetes API: Understanding apiVersion

apiVersion field is the first line of every manifest. While it may seem like a static piece of boilerplate, it is actually the most important instruction you give to the API server. It defines the schema, the validation rules, and the stability of the resource are about to create.

Kubernetes organizes its thousands of parameters into API Groups. The apiVersion string tells the cluster which "folder" and "version" of the API to look in.

There are two distinct patterns for these values:
1. The Core Group
These are the foundational objects of Kubernetes. Because they have existed since the beginning, they do not belong to a named group.
    Format: v1
    Resources: Pod, Service, Namespace, Node, ConfigMap, Secret.
    Example: 
    YAML
    apiVersion: v1
    kind: Service

2. Named Groups
As Kubernetes evolved, new functionality was added via specialized groups. These follow a "Group/Version" structure.
    Format: group.k8s.io/version
    Resources: Deployments, Ingress, CronJobs.
    Example:
    YAML
    apiVersion: apps/v1
    kind: Deployment

The Stability Lifecycle
Version     Stability           Description
v1alpha1    Experimental        May contain bugs. Can be dropped in future releases without warning.
v1beta1     Prerelease          Feature-complete and tested. Safe for non-critical environments.
v1          Stable              Production-ready.   

 
Many resources have migrated from "Beta" to "Stable" over the last few years. Here is the current standard mapping for common resources:
    Workloads: apps/v1 (Deployment, StatefulSet, DaemonSet)
    Batch: batch/v1 (Job, CronJob)
    Networking: networking.k8s.io/v1 (Ingress, NetworkPolicy)
    RBAC: rbac.authorization.k8s.io/v1 (Role, ClusterRole)

# See all resources and their associated API versions
kubectl api-resources

# List all enabled API versions on the server
kubectl api-versions

Thursday, 27 November 2025

Java Garbage Collection (GC): How Modern JVM GC Works, Evolves, and Scales

Garbage Collection is the JVM’s silent guardian. It quietly reclaims memory from objects your application no longer needs—no manual freeing, no memory leaks (well, mostly), no pointer nightmares.

But as applications scale and heap sizes grow into gigabytes or even terabytes, those tiny moments when GC stops your application (known as Stop-The-World pauses) can become the single biggest threat to performance.

To understand why GC pauses happen—and how modern collectors like G1, ZGC, and Shenandoah nearly eliminate them—we need to start with the basics: how Java organises memory.

Java Heap: Where Objects Live and Die
The design of the Java Heap is based on one powerful, observed truth: the "Weak Generational Hypothesis"—that is, most objects die very young. This insight led to Generational Garbage Collection, where the heap is strategically partitioned based on an object's expected lifespan.


GC Roots
This is the starting line for the GC process. An object is only considered "live" if the GC can trace a path to it from one of these roots. They are the application's solid reference points, the objects that must absolutely not be collected:
    Local variables on your thread stacks.
    Static fields of loaded classes.
    Active threads and native JNI references.

Eden:
Every new object you create with new is born here. This is the most volatile area, constantly being collected by the Minor GC. It acts like a nursery where the majority of objects (≈90% or more) are created and die almost instantly, never leaving this space.

Survivor Spaces (S0 / S1):
Objects that managed to survive their first encounter with the Minor GC in Eden are moved here. They ping-pong back and forth between the two small spaces (S0 and S1). Each time an object survives this trip, its "age" counter ticks up, proving its longevity.

Old Generation: 
Objects that successfully pass a predefined age threshold (usually around 15 minor collections) are considered long-lived and are promoted to the Old Generation. This area contains the stable, long-term residents, and consequently, it is collected much less often by a Major GC or Full GC.

Metaspace: 
This area is technically outside the Heap in native system memory. Since Java 8 (it replaced the old PermGen), Metaspace holds the metadata about the classes your application loads—the structure, names, and methods. It's the blueprint archive for your application's code. 

GC Mechanisms: How Garbage is Found and Removed
How does the JVM actually clean up? There are three primary mechanisms that all GCs use in some combination.
Mark & Sweep:
Mark Phase: The GC walks the object graph starting from the GC Roots and marks everything reachable (live).
Sweep Phase: The GC scans the heap and reclaims memory from unmarked (garbage) objects.
The catch? This leaves the heap with Swiss-cheese-like holes, known as fragmentation. This fragmentation can lead to a dreaded Full GC when the JVM can't find a contiguous space large enough for a new object, even if there is technically enough free memory overall.

Mark–Sweep–Compact: Solving the Fragmentation Problem
To fix fragmentation, a third step is added:
    Compact Phase: All live objects are shuffled to one side of the heap, leaving the free space as one large, clean block. This is great for allocation, but compaction takes time, adding significantly to the STW pause.

The Copying Algorithm :
In the Young Generation, the JVM uses a far faster trick: copying. Instead of marking, sweeping, and compacting, it simply copies live objects from the active spaces (Eden + S0) into the empty space (S1). It then wipes the old spaces clean. Copying is naturally compacting and lightning-fast—this is why Minor GCs are usually so quick.

Tri-Color Marking:
For modern GCs (G1, ZGC, Shenandoah) to work concurrently—meaning the application runs while the GC cleans—they use Tri-Color Marking. This helps the GC understand the current state of objects even as application threads (Mutators) are busy changing references.
    White: Unvisited (suspected garbage).
    Gray: Visited, but its object references have not yet been scanned.
    Black: Visited, and all of its references have been scanned (known-live).

To prevent the application from accidentally hiding a live object (the "tri-color invariant" violation), these GCs use write barriers or load barriers—tiny, quick bits of code inserted by the JVM compiler to manage references whenever the application touches memory.

GC Evolution & Timelines:

Java VersionCollectorNotes
Java 1.3Serial GCFirst simple GC
Java 1.4Parallel GCMultithreaded, throughput-focused
Java 5CMSFirst low-pause GC
Java 7G1 (experimental)Region-based innovation
Java 9G1 defaultCMS deprecated
Java 11ZGC (experimental)Sub-millisecond pauses
Java 15ZGC GAProduction-ready
Java 12–15ShenandoahUltra-low latency
Java 14CMS removedEnd of an era


Serial GC:
Think of Serial GC as a single janitor who locks the doors before cleaning.
    The Vibe: Simple and sequential. It uses a single thread for all collection work.
    The Cost: This is the definition of a Stop-The-World (STW) pause. Every single application thread must halt for both Young and Old generation collections.
    Best For: Tiny stuff. We're talking small clients, embedded systems, or containers with heaps well under 100MB. If you have plenty of CPU cores, don't use this.
    Enable: -XX:+UseSerialGC

Parallel GC:
Parallel GC is the natural evolution of Serial: "If one thread is slow, use ten!"
    The Goal: It’s nicknamed the Throughput Collector because its mission is to maximize the total amount of work your application gets done. It does this by using multiple GC threads to speed up the collection phase.
    The Tradeoff: It still pauses the world (it’s an STW collector), but the pauses are much shorter than Serial. However, on multi-gigabyte heaps, these pauses can still be noticeable—sometimes hitting the half-second or even one-second mark.
    Mechanism: It uses multi-threaded Mark–Sweep–Compact for both Young and Old collections.
    Enable: -XX:+UseParallelGC

CMS (Concurrent Mark Sweep):
CMS was Java's first serious attempt at achieving low latency. It was a game-changer but came with baggage.
    The Breakthrough: It figured out how to do most of the marking concurrently—meaning the GC was tracking objects while your application threads were still running. This dramatically minimized the longest STW pauses.
    The Flaw: CMS was a non-compacting collector. Over time, the heap became terribly fragmented (Swiss cheese holes!). Eventually, the JVM would fail to find a large enough contiguous block for a new object, leading to a catastrophic, hours-long STW Full GC just to compact everything.
    Status: Due to its complexity and fragmentation issues, CMS is considered legacy—it was deprecated in Java 9 and removed entirely in Java 14.
    Enable: -XX:+UseConcMarkSweepGC
    
G1 GC (Garbage-First):
G1 is the modern standard, a massive leap forward that shifted the focus from the whole heap to manageable regions.
    Core Idea: Instead of treating the heap as three fixed blocks (Eden/Survivor/Old), G1 carves it up into ≈2048 fixed-size regions. These regions dynamically switch roles (Young, Old, Humongous) as needed.
    Pause Prediction: G1 tracks which regions have the most garbage (the best "return on investment"). It follows the Garbage-First principle, prioritizing those regions to meet your specified pause time goal (e.g., "I promise to pause no longer than 200ms").
    Collection: It uses Evacuation (copying) to move live objects out of the selected regions. This means it compacts memory as it cleans, eliminating the fragmentation nightmare that plagued CMS. G1 is the default collector since Java 9 for a reason: it's a great all-around performer.
    Enable: -XX:+UseG1GC

ZGC (Ultra-Low Latency):
ZGC is the future. Its design goal was radical: pause times must be independent of the heap size. You can run a TB-sized heap, and your application will pause for the same fraction of a millisecond as a 1GB heap.
    Concurrent Everything: It does marking, relocation, and reference processing all concurrently with the application.
    The Magic: It achieves this via Colored Pointers and Load Barriers. The GC can literally move an object while your application is using it. When your code tries to access the object, the Load Barrier briefly intercepts the call, corrects the old pointer to the object's new location, and lets the application continue. The pause for this fix-up is incredibly brief.
    Pause Time: Guaranteed ≈1−3ms pauses. This is the choice for extreme low-latency and massive memory systems.
    Enable: -XX:+UseZGC

Shenandoah:
Developed by Red Hat (now part of OpenJDK), Shenandoah shares ZGC's goal of achieving ultra-low pause times independent of heap size.
    Similarities: It is also region-based and uses a concurrent approach.
    Distinction: Shenandoah's key innovation is its highly optimized concurrent compaction. It can perform memory consolidation while your application is fully running, ensuring the heap stays compact and healthy without any long STW events.
    Best For: Scenarios similar to ZGC—very large heaps and demanding latency requirements.
    Enable: -XX:+UseShenandoahGC
    
 







Friday, 6 December 2024

SLAs: The "Nines" of Uptime

Ever wondered how your favorite online services stay up and running almost all the time? A lot of it comes down to a Service Level Agreement (SLA). An SLA is a contract between a service provider and a customer that defines the level of service to be expected. It's a key document that sets expectations and provides recourse if those expectations aren't met. For an IT support person, understanding SLAs is crucial because it helps you know what to prioritize and what's at stake when something goes down.

What's the Big Deal with "Uptime"?
Uptime is the most common metric used in SLAs. It refers to the percentage of time a service is operational and available for use. The higher the percentage, the less downtime a service experiences. This is often expressed in "nines"—99%, 99.9%, and so on.

The difference between a few "nines" might seem insignificant, but it has a huge impact on real-world availability. A service with an uptime of 99% sounds good on paper, but when you break it down, it means the service can be down for over three and a half days a year. For a business that relies on a critical application, that amount of downtime can be catastrophic.

Decoding the "Nines" :
To truly grasp the impact of each percentage, let's look at a breakdown of the downtime allowed for different uptime levels:
SLA Uptime                  Daily Downtime    Weekly Downtime    Yearly Downtime
99% (Two Nines)           14.4 minutes         1.68 hours                   3.65 days
99.9% (Three Nines)     1.44 minutes        10.08 minutes            8.77 hours
99.99% (Four Nines)    8.64 seconds        1.01 minutes                52.56 minutes
99.999% (Five Nines)   0.86 seconds       6.05 seconds                5.26 minutes
99.9999% (Six Nines)   0.086 seconds     0.61 seconds                31.54 seconds

As an IT support professional, these numbers should be your north star. If you're managing a system with a 99.9% SLA, you know that every minute of downtime counts. A service outage that lasts just a few minutes could put you in breach of the SLA, potentially leading to financial penalties for your company. This is why you'll often hear about the concept of "five nines" (99.999%) in enterprise-level services. It represents a level of reliability that is almost perfect.

Why It Matters to You, the IT Pro?
Understanding SLAs isn't just about memorizing a table; it's about shifting your mindset. It helps you:
    Prioritize incidents: A critical system with a strict SLA must be addressed immediately.
    Manage expectations: You can communicate realistic recovery times to stakeholders based on the SLA.
    Advocate for resources: If a service with a high-stakes SLA is struggling, you can use the numbers to justify the need for better infrastructure or tools.
    
    

Friday, 22 November 2024

Prometheus vs InfluxDB: Choosing the Best Time-Series Database for Monitoring

When it comes to monitoring the performance and health of your applications, systems, and infrastructure, time-series data plays a key role. A time-series database is essential for managing and analyzing this data effectively.

InfluxDB and Prometheus are two of the most popular open-source tools for handling time-series data. They are both widely used, but each serves a different purpose and has its own advantages. InfluxDB has a broad range of time-series data storage capabilities, including system metrics and IoT data, while Prometheus is popular for monitoring real-time metrics and cloud-native environments.


What Is Prometheus?
Prometheus is an open-source monitoring and alerting toolkit developed by SoundCloud and later contributed to the Cloud Native Computing Foundation (CNCF). It is widely adopted for monitoring the health of applications, microservices, containers, and infrastructure, particularly in Kubernetes-based environments.
Prometheus collects and stores metrics in a time-series format, where each time-series is identified by a metric name and associated labels (key-value pairs). Prometheus uses a pull-based model to scrape data from various sources like application endpoints, servers, or exporters.
Key Features of Prometheus:
    Pull-based model: Prometheus scrapes metrics from configured endpoints, which allows for a decentralized and flexible architecture.
    PromQL: A powerful query language designed specifically for time-series data. PromQL allows for aggregating, filtering, and visualizing metrics.
    Alerting: Built-in alerting capabilities through Alertmanager, enabling users to define alert rules based on metric values.
    Data retention: Prometheus stores data on disk using a custom, time-series optimized format and allows you to configure retention periods manually.
    Integration with Grafana: Prometheus integrates seamlessly with Grafana to visualize metrics on customizable dashboards.
    
What Is InfluxDB?
InfluxDB is another popular open-source time-series database developed by InfluxData. Unlike Prometheus, which is primarily focused on monitoring and alerting, InfluxDB is a more general-purpose time-series database that can handle various types of time-series data, including metrics, events, logs, and IoT data.
InfluxDB follows a push-based model, where data is written to the database using an HTTP API or other ingestion methods like Telegraf (an open-source agent for collecting, processing, and sending metrics).
Key Features of InfluxDB:
    Push-based model: Data is pushed to InfluxDB either via its API or through Telegraf agents, making it suitable for scenarios where the data is generated by external systems or devices.
    InfluxQL and Flux: InfluxDB uses InfluxQL, a SQL-like query language, for querying time-series data. Flux is a more powerful, functional query language that enables complex transformations, aggregations, and analytics.
    Continuous queries: InfluxDB supports continuous queries to automatically downsample and aggregate data over time, making it ideal for long-term data retention and historical analysis.
    Retention policies: InfluxDB allows users to define automatic retention policies, meaning older data can be automatically dropped or downsampled as needed.
    Clustering and High Availability: InfluxDB Enterprise provides support for clustering, data replication, and high availability (HA), enabling horizontal scaling for large-scale environments.
    Integration with Grafana: Like Prometheus, InfluxDB integrates with Grafana for visualizing time-series data on interactive dashboards.

Prometheus vs InfluxDB: A Detailed Comparison

Feature/AspectPrometheusInfluxDB
Data ModelPull-based with metric names and labelsPush-based with measurements, tags, and fields
Data Collection ModelPull-based (scraping)Push-based (data is sent to InfluxDB)
Query LanguagePromQL (Prometheus Query Language)InfluxQL (SQL-like) / Flux (more advanced)
AlertingBuilt-in alerting with AlertmanagerBuilt-in alerting with Kapacitor
Data RetentionConfigurable retention period through prometheus.ymlAutomatic retention policies and continuous queries
ScalabilityFederation for horizontal scaling, no native clustering in open-sourceClustering and horizontal scaling available in Enterprise version
StorageTime-series optimized format with local storageTime-series optimized with Time-Structured Merge Tree (TSM)
Integration with GrafanaSeamless integration with Grafana for dashboardsSeamless integration with Grafana for dashboards
Best Use CasesMonitoring metrics for cloud-native and
containerized applications, particularly in Kubernetes environments
General-purpose time-series storage for metrics,
IoT, logs, and events
EcosystemStrong ecosystem with exporters for various servicesPart of InfluxData stack (Telegraf, Kapacitor, Chronograf)
CostFree and open-source,
though scaling may require additional components like Cortex or Thanos
Free open-source version,
but scaling and clustering require Enterprise version

 

Saturday, 16 November 2024

HAProxy Log Rotation Not Working? Here’s How to Fix It

When running HAProxy in production, it's crucial that log files are rotated properly to prevent excessive disk usage and system slowdowns. If HAProxy logs are not rotating as expected, it could lead to your disk filling up, affecting the performance and reliability of your system.
If your HAProxy logs are not rotating, it could be due to several possible reasons.
 In this post, we'll walk through the most common causes of log rotation issues, how to troubleshoot them, and provide a real-world use case with a solution.

1.Logrotate Configuration Missing or Incorrect
HAProxy typically uses logrotate to handle log file rotation. If your log files are not rotating, it could be due to a missing or misconfigured logrotate configuration.
How to Check Logrotate Configuration:
Ensure there is a logrotate configuration file for HAProxy in /etc/logrotate.d/
It should look similar to the following:
 /var/log/haproxy.log {
        daily
        missingok
        rotate 7
        compress
        notifempty
        create 0640 haproxy adm
        sharedscripts
        postrotate
     /etc/init.d/haproxy reload > /dev/null 2>/dev/null || true
        endscript
    }


Explanation of Directives:
daily: Rotate the log files daily. You can also use weekly, monthly, etc., depending on your requirements.
rotate 7: Keep 7 backup log files before deleting the oldest.
compress: Compress old log files to save disk space.
create 0640 haproxy adm: This ensures that new log files are created with proper permissions (0640), and the owner is set to haproxy, with the group as adm.
postrotate: This ensures that HAProxy is reloaded after log rotation to begin writing to the new log file. If HAProxy is still writing to the old log file, logrotate will not be able to rename the rotated file.

Troubleshooting:
If the logrotate configuration is missing or incorrectly configured, you can either create or update the configuration file as shown above.
To check if logrotate is working correctly, run the following command to simulate the log rotation process:
sudo logrotate -d /etc/logrotate.conf
This command will display what logrotate would do, but will not actually rotate any logs. This is useful for troubleshooting.

2. Permissions Issues
If the HAProxy log files are not being written to or rotated due to permission issues, you need to verify that HAProxy has write access to its log file and the directory.
Check the permissions of /var/log/haproxy.log and ensure the user HAProxy runs as (usually haproxy) has the correct permissions:
ls -l /var/log/haproxy.log
Check that the logrotate user (usually root) has the necessary permissions to rotate the file.
If permissions are incorrect, adjust them with chown and chmod:
sudo chown haproxy:adm /var/log/haproxy.log
sudo chmod 0640 /var/log/haproxy.log


3. Log Output Configuration in HAProxy
HAProxy must be configured to log to a file (e.g., /var/log/haproxy.log). Ensure your HAProxy configuration includes proper logging directives:
In /etc/haproxy/haproxy.cfg, make sure you have something like the following:
global
    log /dev/log local0
defaults
    log     global
    option  httplog

This tells HAProxy to log to the syslog facility local0, which is often associated with the HAProxy logs. If this is not set correctly, HAProxy may not be logging to the expected location.

4. Logfile Being Open by HAProxy Process
If the HAProxy process is holding the log file open (e.g., if HAProxy is still running with the old log file after rotation), logrotate might fail to rename the file. You can ensure that HAProxy is properly reloading by sending a SIGHUP signal to HAProxy, or by using the postrotate script in the logrotate config (mentioned above).
To manually reload HAProxy, you can:
sudo systemctl reload haproxy
or
sudo service haproxy reload

5. Logrotate Not Running
If logrotate is not running automatically (e.g., if the cron job for logrotate is not configured or working), the logs will not rotate.
Check cron jobs: Ensure that the logrotate cron job is enabled. You can check cron jobs by listing them with:
crontab -l
Alternatively, check if the logrotate service is running (on systems that use systemd):
systemctl status logrotate
To test logrotate manually, run:
sudo logrotate /etc/logrotate.conf

6. Disk Space Issues
If your disk is full, logrotate may not be able to create new log files or rotate old ones. You can check disk usage with:
df -h
If the disk is full, free up some space or increase the disk size.


Monday, 28 October 2024

Kubernetes Engine (OKE) supports the following versions of Kubernetes for new clusters

Container Engine for Kubernetes now supports Kubernetes version 1.28.10, in addition to versions 1.30.1 and 1.29.1. Please be aware that with the addition of support for version 1.28.10, Container Engine for Kubernetes will officially discontinue support for version 1.28.2 starting October 8, 2024.

Kubernetes Engine (OKE) supports the following versions of Kubernetes for new clusters: 

Kubernetes Minor VersionKubernetes Patch Version
Supported by OKE
Upstream Minor Version
Release Date
Upstream Minor Version
End-of-life date
OKE
Release Date
OKE End-of-life Date
1.31.30.12024-04-172025-06-282024-07-2330 days after 1.33 OKE Release Date (planned)
1.291.29.12023-12-132025-02-282024-03-2830 days after 1.32 OKE Release Date (planned)
1.281.28.102023-08-152024-10-282024-09-0330 days after 1.31 OKE Release Date (planned)
1.281.28.22023-08-152024-10-282023-12-192024-10-08

Ref: https://docs.oracle.com/en-us/iaas/releasenotes/conteng/conteng-K8s-1-28-10-support.htm

Wednesday, 19 July 2023

CIDR: The Modern Way to Handle Subnets

What is CIDR?
CIDR (Classless Inter-Domain Routing) is a method for allocating IP addresses and routing that replaced the old "classful" system. It uses a slash notation to specify how many bits are used for the network portion.
Format: IP Address/Network Bits
Example: 192.168.1.0/24

How CIDR Notation Works
The Number After the Slash
The /24 tells us how many bits (from left) represent the network:
192.168.1.0/24
├── Network bits: 24 (first 24 bits)
└── Host bits: 8 (remaining 8 bits)

Binary view:
11000000.10101000.00000001.00000000
|--------Network (24 bits)-------|Host|

Common CIDR Examples
CIDR    Subnet Mask               Network Bits    Host Bits    Available IPs
/8     255.0.0.0          8       24     16,777,214
/16    255.255.0.0        16      16     65,534
/24    255.255.255.0      24      8      254
/25    255.255.255.128    25      7      126
/26    255.255.255.192    26      6      62
/27    255.255.255.224    27      5      30
/28    255.255.255.240    28      4      14
/30    255.255.255.252    30      2       2

How CIDR Helps with Subnetting
1. Flexible Subnet Sizing
Instead of fixed Class A, B, C sizes, you can create any size you need:
Old way (Classful):
- Class C: 192.168.1.0 = exactly 254 hosts
- No flexibility

New way (CIDR):
- /25 = 126 hosts
- /26 = 62 hosts  
- /27 = 30 hosts
- /28 = 14 hosts

2. Efficient Address Usage
Example: You need 50 IP addresses
Without CIDR: Forced to use Class C (254 IPs) - waste 204 addresses
With CIDR: Use /26 (62 IPs) - waste only 12 addresses

3. Easy Subnet Calculation
Subnetting 192.168.1.0/24 into smaller subnets:
Original: 192.168.1.0/24 (254 hosts)

Split into /25:
├── 192.168.1.0/25   (126 hosts: .1-.126)
└── 192.168.1.128/25 (126 hosts: .129-.254)

Split into /26:
├── 192.168.1.0/26   (62 hosts: .1-.62)
├── 192.168.1.64/26  (62 hosts: .65-.126)
├── 192.168.1.128/26 (62 hosts: .129-.190)
└── 192.168.1.192/26 (62 hosts: .193-.254)

4. Quick Binary Math
To find subnet info:
Network bits: First X bits (from CIDR)
Host bits: Remaining 32-X bits
Subnet size: 2^(host bits) - 2
Subnets possible: 2^(borrowed bits)

Practical CIDR Examples
Example 1: Office Network Planning

Company gets: 10.0.0.0/16

Departments needed:
- Sales (100 users): 10.0.1.0/25 (126 IPs)
- Engineering (200 users): 10.0.2.0/24 (254 IPs)  
- HR (20 users): 10.0.3.0/27 (30 IPs)
- Printers (10 devices): 10.0.4.0/28 (14 IPs)

Example 2: ISP Address Allocation
ISP has: 203.0.113.0/24

Customer allocations:
- Large business: 203.0.113.0/26 (62 IPs)
- Medium business: 203.0.113.64/27 (30 IPs)
- Small business: 203.0.113.96/28 (14 IPs)
- Home users: 203.0.113.112/28 to 203.0.113.240/28

CIDR vs Old Classful System
Old Way (Classful)
Class A: /8  - 16M addresses (usually too big)
Class B: /16 - 65K addresses (often too big) 
Class C: /24 - 254 addresses (often too small)

New Way (CIDR)
Any size from /1 to /32
Perfect fit for actual needs
No address waste

Quick CIDR Calculation Tricks
Finding Subnet Size
Formula: 2^(32-CIDR) - 2

/24: 2^(32-24) - 2 = 2^8 - 2 = 254 usable IPs
/26: 2^(32-26) - 2 = 2^6 - 2 = 62 usable IPs
/30: 2^(32-30) - 2 = 2^2 - 2 = 2 usable IPs

Finding Number of Subnets
To subnet /24 into /26:
Borrowed bits = 26 - 24 = 2
Number of subnets = 2^2 = 4 subnets

Real-World Benefits
Reduced Routing Tables - ISPs can aggregate routes
Flexible Allocation - Match network size to actual needs
Address Conservation - Use only what you need
Simplified Management - Consistent notation across all networks



Saturday, 15 April 2023

Key Differences Between Proxy, Reverse Proxy, and Load Balancer

Feature Proxy Server Reverse Proxy Load Balancer
Primary Purpose Intermediary between client and server Intermediary between client
and backend servers
Distributes traffic across servers
to optimize resource usage
Direction of Traffic Client → Proxy → Server Client → Reverse Proxy → Server Client → Load Balancer → Server(s)
Key Use Cases Privacy, content filtering, caching Security, load balancing,
SSL termination
High availability, scalability, redundancy
Security Hides client’s identity from server Hides server’s identity from clients Improves system availability by balancing loads
Performance Focus Caching and speed optimization Load balancing, SSL offloading,
 security
Traffic distribution and resource optimization
Example Tools Squid Proxy, Charles Proxy,
 HAProxy
Nginx, Apache HTTP Server (mod_proxy),
 HAProxy, Traefik
Nginx, HAProxy, F5 BIG-IP, AWS ELB, Traefik