mirror of
https://github.com/harivansh-afk/system-design.git
synced 2026-04-15 08:03:45 +00:00
ui
This commit is contained in:
parent
89ce6a9542
commit
d35609deb2
16 changed files with 360 additions and 256 deletions
|
|
@ -0,0 +1 @@
|
|||
Tue Jan 6 23:38:01 IST 2026
|
||||
7
.claude/hooks/session-end.log
Normal file
7
.claude/hooks/session-end.log
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[2026-01-06 23:38:01] Hook triggered with input: {"session_id":"51726d25-d24f-48e8-bb5d-a775dc434ee4","transcript_path":"/Users/rathi/.claude/projects/-Users-rathi-Documents-GitHub-infra-web/51726d25-d24f-48e8-bb5d-a775dc434ee4.jsonl","cwd":"/Users/rathi/Documents/GitHub/infra-web","hook_event_name":"SessionEnd","reason":"prompt_input_exit"}
|
||||
[2026-01-06 23:38:01] Transcript: /Users/rathi/.claude/projects/-Users-rathi-Documents-GitHub-infra-web/51726d25-d24f-48e8-bb5d-a775dc434ee4.jsonl
|
||||
[2026-01-06 23:38:01] Session ID: 51726d25-d24f-48e8-bb5d-a775dc434ee4
|
||||
[2026-01-06 23:38:01] Project dir: /Users/rathi/Documents/GitHub/infra-web
|
||||
[2026-01-06 23:38:01] Lock file created
|
||||
[2026-01-06 23:38:01] Transcript line count: 4
|
||||
[2026-01-06 23:38:01] Session too short, skipping retrospective
|
||||
3
README.md
Normal file
3
README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
## System design for distributed systems
|
||||
|
||||
This repo was built by claude code and codex for me before a system design interview
|
||||
|
|
@ -11,25 +11,29 @@
|
|||
interface Option {
|
||||
id: string;
|
||||
name: string;
|
||||
color: string;
|
||||
icon: string;
|
||||
description?: string;
|
||||
color?: string;
|
||||
icon?: string;
|
||||
whenToUse: string[];
|
||||
whenNotToUse: string[];
|
||||
realWorldExample?: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
options: Option[];
|
||||
features: ComparisonItem[];
|
||||
features?: ComparisonItem[];
|
||||
}
|
||||
|
||||
let { title, subtitle = '', options, features }: Props = $props();
|
||||
let { title = '', subtitle = '', options, features = [] }: Props = $props();
|
||||
let selectedOption = $state<string | null>(null);
|
||||
|
||||
function getIcon(iconName: string) {
|
||||
return (Icons as Record<string, typeof Icons.Box>)[iconName] || Icons.Box;
|
||||
type IconComponent = typeof Icons.Box;
|
||||
|
||||
function getIcon(iconName?: string): IconComponent {
|
||||
const iconMap = Icons as unknown as Record<string, IconComponent>;
|
||||
return (iconName ? iconMap[iconName] : null) ?? Icons.Box;
|
||||
}
|
||||
|
||||
function getCellContent(value: string | boolean) {
|
||||
|
|
@ -44,66 +48,104 @@
|
|||
|
||||
<div class="space-y-6">
|
||||
<!-- Header -->
|
||||
<div>
|
||||
<h2 class="section-title">{title}</h2>
|
||||
{#if subtitle}
|
||||
<p class="section-subtitle">{subtitle}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{#if title || subtitle}
|
||||
<div>
|
||||
{#if title}
|
||||
<h2 class="section-title">{title}</h2>
|
||||
{/if}
|
||||
{#if subtitle}
|
||||
<p class="section-subtitle">{subtitle}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Comparison Table -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left p-4 bg-surface-900 border-b border-surface-700 text-surface-400 font-medium">
|
||||
Feature
|
||||
</th>
|
||||
{#each options as option}
|
||||
{@const Icon = getIcon(option.icon)}
|
||||
<th
|
||||
class="p-4 bg-surface-900 border-b border-surface-700 text-center cursor-pointer hover:bg-surface-800 transition-colors"
|
||||
onclick={() => selectedOption = selectedOption === option.id ? null : option.id}
|
||||
>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<div
|
||||
class="w-10 h-10 rounded-lg flex items-center justify-center"
|
||||
style="background-color: {option.color}20; color: {option.color}"
|
||||
>
|
||||
<Icon class="w-6 h-6" />
|
||||
</div>
|
||||
<span class="font-medium text-surface-100">{option.name}</span>
|
||||
</div>
|
||||
{#if features.length > 0}
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left p-4 bg-surface-900 border-b border-surface-800 text-surface-400 font-medium">
|
||||
Feature
|
||||
</th>
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each features as feature, i}
|
||||
<tr class="border-b border-surface-800 hover:bg-surface-900/50">
|
||||
<td class="p-4">
|
||||
<div class="font-medium text-surface-200">{feature.feature}</div>
|
||||
{#if feature.description}
|
||||
<div class="text-sm text-surface-500 mt-1">{feature.description}</div>
|
||||
{/if}
|
||||
</td>
|
||||
{#each options as option}
|
||||
{@const cell = getCellContent(feature.values[option.id])}
|
||||
<td class="p-4 text-center {cell.class}">
|
||||
{#if cell.type === 'check'}
|
||||
<Icons.Check class="w-5 h-5 mx-auto" />
|
||||
{:else if cell.type === 'x'}
|
||||
<Icons.X class="w-5 h-5 mx-auto" />
|
||||
{:else}
|
||||
<span class="text-sm">{cell.value}</span>
|
||||
{/if}
|
||||
</td>
|
||||
{@const Icon = getIcon(option.icon)}
|
||||
<th
|
||||
class="p-4 bg-surface-900 border-b border-surface-800 text-center cursor-pointer hover:bg-surface-800 transition-colors"
|
||||
onclick={() => selectedOption = selectedOption === option.id ? null : option.id}
|
||||
>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<div
|
||||
class="w-10 h-10 rounded-lg flex items-center justify-center border border-surface-700 bg-surface-800"
|
||||
style={option.color ? `background-color: ${option.color}20; color: ${option.color}` : ''}
|
||||
>
|
||||
<Icon class="w-6 h-6" />
|
||||
</div>
|
||||
<span class="font-medium text-surface-100">{option.name}</span>
|
||||
</div>
|
||||
</th>
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each features as feature, i}
|
||||
<tr class="border-b border-surface-800 hover:bg-surface-900/50">
|
||||
<td class="p-4">
|
||||
<div class="font-medium text-surface-200">{feature.feature}</div>
|
||||
{#if feature.description}
|
||||
<div class="text-sm text-surface-500 mt-1">{feature.description}</div>
|
||||
{/if}
|
||||
</td>
|
||||
{#each options as option}
|
||||
{@const cell = getCellContent(feature.values[option.id])}
|
||||
<td class="p-4 text-center {cell.class}">
|
||||
{#if cell.type === 'check'}
|
||||
<Icons.Check class="w-5 h-5 mx-auto" />
|
||||
{:else if cell.type === 'x'}
|
||||
<Icons.X class="w-5 h-5 mx-auto" />
|
||||
{:else}
|
||||
<span class="text-sm">{cell.value}</span>
|
||||
{/if}
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="grid sm:grid-cols-2 gap-4">
|
||||
{#each options as option}
|
||||
{@const Icon = getIcon(option.icon)}
|
||||
{@const active = selectedOption === option.id}
|
||||
<button
|
||||
class="text-left p-4 rounded-xl border transition-colors"
|
||||
class:bg-surface-900={!active}
|
||||
class:border-surface-800={!active}
|
||||
class:hover:border-surface-700={!active}
|
||||
class:bg-surface-800={active}
|
||||
class:border-surface-700={active}
|
||||
onclick={() => selectedOption = active ? null : option.id}
|
||||
>
|
||||
<div class="flex items-start gap-3">
|
||||
<div
|
||||
class="w-10 h-10 rounded-lg flex items-center justify-center border border-surface-700 bg-surface-800"
|
||||
style={option.color ? `background-color: ${option.color}20; color: ${option.color}` : ''}
|
||||
>
|
||||
<Icon class="w-6 h-6" />
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-surface-100">{option.name}</div>
|
||||
{#if option.description}
|
||||
<div class="text-sm text-surface-500 mt-0.5">{option.description}</div>
|
||||
{/if}
|
||||
</div>
|
||||
<Icons.ChevronDown class="w-4 h-4 mt-1 text-surface-500 transition-transform {active ? 'rotate-180' : ''}" />
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- When to Use / When Not to Use Panels -->
|
||||
{#if selectedOption}
|
||||
|
|
|
|||
|
|
@ -31,9 +31,11 @@
|
|||
return nodes.find(n => n.id === id);
|
||||
}
|
||||
|
||||
function getIcon(iconName?: string) {
|
||||
if (!iconName) return Icons.HelpCircle;
|
||||
return (Icons as Record<string, typeof Icons.Box>)[iconName] || Icons.HelpCircle;
|
||||
type IconComponent = typeof Icons.Box;
|
||||
|
||||
function getIcon(iconName?: string): IconComponent {
|
||||
const iconMap = Icons as unknown as Record<string, IconComponent>;
|
||||
return (iconName ? iconMap[iconName] : null) ?? (Icons.HelpCircle as IconComponent);
|
||||
}
|
||||
|
||||
function answer(nodeId: string, isYes: boolean) {
|
||||
|
|
|
|||
|
|
@ -32,9 +32,11 @@
|
|||
let isPlaying = $state(autoPlay);
|
||||
let intervalId: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
function getIcon(iconName?: string) {
|
||||
if (!iconName) return Icons.Circle;
|
||||
return (Icons as Record<string, typeof Icons.Box>)[iconName] || Icons.Circle;
|
||||
type IconComponent = typeof Icons.Box;
|
||||
|
||||
function getIcon(iconName?: string): IconComponent {
|
||||
const iconMap = Icons as unknown as Record<string, IconComponent>;
|
||||
return (iconName ? iconMap[iconName] : null) ?? (Icons.Circle as IconComponent);
|
||||
}
|
||||
|
||||
function nextStep() {
|
||||
|
|
@ -202,6 +204,7 @@
|
|||
{#each steps as _, i}
|
||||
<button
|
||||
onclick={() => goToStep(i)}
|
||||
aria-label={"Go to step " + (i + 1)}
|
||||
class="w-2 h-2 rounded-full transition-all duration-200"
|
||||
class:bg-surface-100={i === currentStep}
|
||||
class:w-4={i === currentStep}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,11 @@
|
|||
onclick
|
||||
}: Props = $props();
|
||||
|
||||
function getIcon(iconName: string) {
|
||||
return (Icons as Record<string, typeof Icons.Box>)[iconName] || Icons.Box;
|
||||
type IconComponent = typeof Icons.Box;
|
||||
|
||||
function getIcon(iconName: string): IconComponent {
|
||||
const iconMap = Icons as unknown as Record<string, IconComponent>;
|
||||
return iconMap[iconName] || Icons.Box;
|
||||
}
|
||||
|
||||
const Icon = $derived(getIcon(icon));
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@
|
|||
import * as Icons from 'lucide-svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
|
||||
function getIcon(iconName: string) {
|
||||
return (Icons as Record<string, typeof Icons.Home>)[iconName] || Icons.Circle;
|
||||
type IconComponent = typeof Icons.Box;
|
||||
|
||||
function getIcon(iconName: string): IconComponent {
|
||||
const iconMap = Icons as unknown as Record<string, IconComponent>;
|
||||
return iconMap[iconName] || (Icons.Circle as IconComponent);
|
||||
}
|
||||
|
||||
function toggleSection(id: string) {
|
||||
|
|
|
|||
|
|
@ -54,8 +54,11 @@
|
|||
{ label: 'Which Database?', href: '/decisions/which-database', icon: 'HelpCircle' }
|
||||
];
|
||||
|
||||
function getIcon(iconName: string) {
|
||||
return (Icons as Record<string, typeof Icons.Box>)[iconName] || Icons.Box;
|
||||
type IconComponent = typeof Icons.Box;
|
||||
|
||||
function getIcon(iconName: string): IconComponent {
|
||||
const iconMap = Icons as unknown as Record<string, IconComponent>;
|
||||
return iconMap[iconName] || Icons.Box;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,23 @@
|
|||
{ step: 5, title: 'Container creation', desc: 'Kubelet tells runtime to start containers' },
|
||||
{ step: 6, title: 'Running', desc: 'Pod running, kube-proxy sets up networking' }
|
||||
];
|
||||
|
||||
const accent = {
|
||||
blue: { badge: 'bg-blue-500/20 text-blue-300', dot: 'bg-blue-500', text: 'text-blue-400', panel: 'bg-blue-500/20 border border-blue-500/30' },
|
||||
purple: { badge: 'bg-purple-500/20 text-purple-300', dot: 'bg-purple-500', text: 'text-purple-400', panel: 'bg-purple-500/20 border border-purple-500/30' },
|
||||
green: { badge: 'bg-green-500/20 text-green-300', dot: 'bg-green-500', text: 'text-green-400', panel: 'bg-green-500/20 border border-green-500/30' },
|
||||
orange: { badge: 'bg-orange-500/20 text-orange-300', dot: 'bg-orange-500', text: 'text-orange-400', panel: 'bg-orange-500/20 border border-orange-500/30' },
|
||||
cyan: { badge: 'bg-cyan-500/20 text-cyan-300', dot: 'bg-cyan-500', text: 'text-cyan-400', panel: 'bg-cyan-500/20 border border-cyan-500/30' },
|
||||
yellow: { badge: 'bg-yellow-500/20 text-yellow-300', dot: 'bg-yellow-500', text: 'text-yellow-400', panel: 'bg-yellow-500/20 border border-yellow-500/30' },
|
||||
pink: { badge: 'bg-pink-500/20 text-pink-300', dot: 'bg-pink-500', text: 'text-pink-400', panel: 'bg-pink-500/20 border border-pink-500/30' },
|
||||
gray: { badge: 'bg-surface-800 text-surface-300 border border-surface-700', dot: 'bg-surface-600', text: 'text-surface-300', panel: 'bg-surface-800/60 border border-surface-800' }
|
||||
} as const;
|
||||
|
||||
type AccentKey = keyof typeof accent;
|
||||
|
||||
function getAccent(color: string): (typeof accent)[AccentKey] {
|
||||
return accent[(color in accent ? color : 'gray') as AccentKey];
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
|
@ -126,27 +143,27 @@
|
|||
</svelte:head>
|
||||
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<h1 class="text-3xl font-bold text-white mb-2">Kubernetes Architecture</h1>
|
||||
<p class="text-gray-400 mb-8">Understanding the control plane, worker nodes, and how they work together</p>
|
||||
<h1 class="text-3xl font-bold text-surface-100 mb-2">Kubernetes Architecture</h1>
|
||||
<p class="text-surface-400 mb-8">Understanding the control plane, worker nodes, and how they work together</p>
|
||||
|
||||
<!-- Overview Diagram -->
|
||||
<section class="mb-12">
|
||||
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
|
||||
<div class="card">
|
||||
<div class="grid md:grid-cols-2 gap-8">
|
||||
<!-- Control Plane -->
|
||||
<div class="p-4 rounded-xl bg-blue-500/10 border border-blue-500/30">
|
||||
<h3 class="text-lg font-semibold text-blue-400 mb-4 text-center">Control Plane</h3>
|
||||
<p class="text-gray-400 text-sm text-center mb-4">The brain - makes decisions about the cluster</p>
|
||||
<p class="text-surface-400 text-sm text-center mb-4">The brain - makes decisions about the cluster</p>
|
||||
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
{#each controlPlaneComponents.slice(0, 4) as component}
|
||||
<button
|
||||
class="p-3 rounded-lg bg-gray-800/50 border border-gray-600 hover:border-gray-400 transition-all text-left"
|
||||
class="p-3 rounded-lg bg-surface-900 border border-surface-800 hover:border-surface-700 transition-colors text-left"
|
||||
class:border-blue-500={selectedComponent === component.id}
|
||||
on:click={() => selectedComponent = selectedComponent === component.id ? null : component.id}
|
||||
>
|
||||
<div class="text-white font-medium text-sm">{component.name}</div>
|
||||
<div class="text-gray-500 text-xs mt-1 line-clamp-2">{component.description}</div>
|
||||
<div class="text-surface-100 font-medium text-sm">{component.name}</div>
|
||||
<div class="text-surface-500 text-xs mt-1 line-clamp-2">{component.description}</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
@ -155,17 +172,17 @@
|
|||
<!-- Worker Nodes -->
|
||||
<div class="p-4 rounded-xl bg-green-500/10 border border-green-500/30">
|
||||
<h3 class="text-lg font-semibold text-green-400 mb-4 text-center">Worker Nodes</h3>
|
||||
<p class="text-gray-400 text-sm text-center mb-4">The muscle - runs your actual workloads</p>
|
||||
<p class="text-surface-400 text-sm text-center mb-4">The muscle - runs your actual workloads</p>
|
||||
|
||||
<div class="space-y-3">
|
||||
{#each nodeComponents as component}
|
||||
<button
|
||||
class="w-full p-3 rounded-lg bg-gray-800/50 border border-gray-600 hover:border-gray-400 transition-all text-left"
|
||||
class="w-full p-3 rounded-lg bg-surface-900 border border-surface-800 hover:border-surface-700 transition-colors text-left"
|
||||
class:border-green-500={selectedComponent === component.id}
|
||||
on:click={() => selectedComponent = selectedComponent === component.id ? null : component.id}
|
||||
>
|
||||
<div class="text-white font-medium text-sm">{component.name}</div>
|
||||
<div class="text-gray-500 text-xs mt-1">{component.description}</div>
|
||||
<div class="text-surface-100 font-medium text-sm">{component.name}</div>
|
||||
<div class="text-surface-500 text-xs mt-1">{component.description}</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
@ -177,12 +194,12 @@
|
|||
{@const allComponents = [...controlPlaneComponents, ...nodeComponents]}
|
||||
{@const component = allComponents.find(c => c.id === selectedComponent)}
|
||||
{#if component}
|
||||
<div class="mt-6 p-4 bg-gray-700/50 rounded-lg border border-gray-600">
|
||||
<h4 class="text-white font-semibold mb-3">{component.name}</h4>
|
||||
<div class="mt-6 p-4 bg-surface-800/60 rounded-lg border border-surface-800">
|
||||
<h4 class="text-surface-100 font-semibold mb-3">{component.name}</h4>
|
||||
<ul class="grid md:grid-cols-2 gap-2">
|
||||
{#each component.details as detail}
|
||||
<li class="flex items-start gap-2 text-gray-300 text-sm">
|
||||
<span class="text-blue-400 mt-0.5">-</span>
|
||||
<li class="flex items-start gap-2 text-surface-300 text-sm">
|
||||
<span class="text-surface-500 mt-0.5">-</span>
|
||||
{detail}
|
||||
</li>
|
||||
{/each}
|
||||
|
|
@ -195,24 +212,24 @@
|
|||
|
||||
<!-- Control Plane Deep Dive -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl font-semibold text-white mb-4">Control Plane Components</h2>
|
||||
<h2 class="text-xl font-semibold text-surface-100 mb-4">Control Plane Components</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
{#each controlPlaneComponents as component}
|
||||
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
|
||||
<div class="card">
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-white">{component.name}</h3>
|
||||
<p class="text-gray-400 text-sm">{component.description}</p>
|
||||
<h3 class="text-lg font-semibold text-surface-100">{component.name}</h3>
|
||||
<p class="text-surface-400 text-sm">{component.description}</p>
|
||||
</div>
|
||||
<span class="px-3 py-1 rounded-full text-xs font-medium bg-{component.color}-500/20 text-{component.color}-400">
|
||||
<span class={"px-3 py-1 rounded-full text-xs font-medium " + getAccent(component.color).badge}>
|
||||
Control Plane
|
||||
</span>
|
||||
</div>
|
||||
<div class="grid md:grid-cols-2 gap-3">
|
||||
{#each component.details as detail}
|
||||
<div class="flex items-center gap-2 p-2 bg-gray-700/30 rounded-lg text-sm text-gray-300">
|
||||
<span class="w-2 h-2 rounded-full bg-{component.color}-500"></span>
|
||||
<div class="flex items-center gap-2 p-2 bg-surface-800/40 border border-surface-800 rounded-lg text-sm text-surface-300">
|
||||
<span class={"w-2 h-2 rounded-full " + getAccent(component.color).dot}></span>
|
||||
{detail}
|
||||
</div>
|
||||
{/each}
|
||||
|
|
@ -224,10 +241,10 @@
|
|||
|
||||
<!-- Request Flow -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl font-semibold text-white mb-4">How a Pod Gets Scheduled</h2>
|
||||
<p class="text-gray-400 mb-6">Follow the journey from kubectl apply to running pod</p>
|
||||
<h2 class="text-xl font-semibold text-surface-100 mb-4">How a Pod Gets Scheduled</h2>
|
||||
<p class="text-surface-400 mb-6">Follow the journey from kubectl apply to running pod</p>
|
||||
|
||||
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
|
||||
<div class="card">
|
||||
<div class="relative">
|
||||
<div class="absolute left-6 top-0 bottom-0 w-0.5 bg-blue-500/30"></div>
|
||||
<div class="space-y-6">
|
||||
|
|
@ -236,9 +253,9 @@
|
|||
<div class="absolute left-0 w-12 h-12 rounded-full bg-blue-500/20 border-2 border-blue-500 flex items-center justify-center text-blue-400 font-bold">
|
||||
{step.step}
|
||||
</div>
|
||||
<div class="bg-gray-700/50 rounded-lg p-4">
|
||||
<div class="text-white font-medium">{step.title}</div>
|
||||
<div class="text-gray-400 text-sm mt-1">{step.desc}</div>
|
||||
<div class="bg-surface-800/60 border border-surface-800 rounded-lg p-4">
|
||||
<div class="text-surface-100 font-medium">{step.title}</div>
|
||||
<div class="text-surface-400 text-sm mt-1">{step.desc}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
|
@ -249,18 +266,18 @@
|
|||
|
||||
<!-- Pod Lifecycle -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl font-semibold text-white mb-4">Pod Lifecycle</h2>
|
||||
<h2 class="text-xl font-semibold text-surface-100 mb-4">Pod Lifecycle</h2>
|
||||
|
||||
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
|
||||
<div class="card">
|
||||
<div class="flex flex-wrap items-center justify-center gap-4">
|
||||
{#each podLifecycle as phase, i}
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-lg bg-{phase.color}-500/20 border border-{phase.color}-500/30 text-center min-w-[120px]">
|
||||
<div class="text-{phase.color}-400 font-semibold text-sm">{phase.phase}</div>
|
||||
<div class="text-gray-500 text-xs mt-1">{phase.desc}</div>
|
||||
<div class={"p-3 rounded-lg text-center min-w-[120px] " + getAccent(phase.color).panel}>
|
||||
<div class={getAccent(phase.color).text + " font-semibold text-sm"}>{phase.phase}</div>
|
||||
<div class="text-surface-500 text-xs mt-1">{phase.desc}</div>
|
||||
</div>
|
||||
{#if i < podLifecycle.length - 1}
|
||||
<div class="mx-2 text-gray-500">-></div>
|
||||
<div class="mx-2 text-surface-600">-></div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
|
|
@ -270,68 +287,68 @@
|
|||
|
||||
<!-- Key Concepts -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl font-semibold text-white mb-4">Key Kubernetes Objects</h2>
|
||||
<h2 class="text-xl font-semibold text-surface-100 mb-4">Key Kubernetes Objects</h2>
|
||||
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
|
||||
<div class="bg-surface-900 rounded-xl p-4 border border-surface-800">
|
||||
<div class="text-blue-400 font-semibold mb-2">Pod</div>
|
||||
<p class="text-gray-400 text-sm">Smallest deployable unit. One or more containers that share network/storage.</p>
|
||||
<p class="text-surface-400 text-sm">Smallest deployable unit. One or more containers that share network/storage.</p>
|
||||
</div>
|
||||
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
|
||||
<div class="bg-surface-900 rounded-xl p-4 border border-surface-800">
|
||||
<div class="text-green-400 font-semibold mb-2">Deployment</div>
|
||||
<p class="text-gray-400 text-sm">Manages ReplicaSets. Handles rolling updates and rollbacks.</p>
|
||||
<p class="text-surface-400 text-sm">Manages ReplicaSets. Handles rolling updates and rollbacks.</p>
|
||||
</div>
|
||||
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
|
||||
<div class="bg-surface-900 rounded-xl p-4 border border-surface-800">
|
||||
<div class="text-purple-400 font-semibold mb-2">Service</div>
|
||||
<p class="text-gray-400 text-sm">Stable network endpoint for pods. ClusterIP, NodePort, LoadBalancer.</p>
|
||||
<p class="text-surface-400 text-sm">Stable network endpoint for pods. ClusterIP, NodePort, LoadBalancer.</p>
|
||||
</div>
|
||||
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
|
||||
<div class="bg-surface-900 rounded-xl p-4 border border-surface-800">
|
||||
<div class="text-orange-400 font-semibold mb-2">ConfigMap</div>
|
||||
<p class="text-gray-400 text-sm">Non-sensitive configuration data. Injected as env vars or files.</p>
|
||||
<p class="text-surface-400 text-sm">Non-sensitive configuration data. Injected as env vars or files.</p>
|
||||
</div>
|
||||
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
|
||||
<div class="bg-surface-900 rounded-xl p-4 border border-surface-800">
|
||||
<div class="text-red-400 font-semibold mb-2">Secret</div>
|
||||
<p class="text-gray-400 text-sm">Sensitive data like passwords, tokens. Base64 encoded (not encrypted!).</p>
|
||||
<p class="text-surface-400 text-sm">Sensitive data like passwords, tokens. Base64 encoded (not encrypted!).</p>
|
||||
</div>
|
||||
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
|
||||
<div class="bg-surface-900 rounded-xl p-4 border border-surface-800">
|
||||
<div class="text-yellow-400 font-semibold mb-2">Ingress</div>
|
||||
<p class="text-gray-400 text-sm">HTTP routing rules. Path-based routing to Services.</p>
|
||||
<p class="text-surface-400 text-sm">HTTP routing rules. Path-based routing to Services.</p>
|
||||
</div>
|
||||
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
|
||||
<div class="bg-surface-900 rounded-xl p-4 border border-surface-800">
|
||||
<div class="text-cyan-400 font-semibold mb-2">StatefulSet</div>
|
||||
<p class="text-gray-400 text-sm">For stateful apps. Stable network identity and persistent storage.</p>
|
||||
<p class="text-surface-400 text-sm">For stateful apps. Stable network identity and persistent storage.</p>
|
||||
</div>
|
||||
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
|
||||
<div class="bg-surface-900 rounded-xl p-4 border border-surface-800">
|
||||
<div class="text-pink-400 font-semibold mb-2">DaemonSet</div>
|
||||
<p class="text-gray-400 text-sm">Run pod on every node. Used for logging, monitoring agents.</p>
|
||||
<p class="text-surface-400 text-sm">Run pod on every node. Used for logging, monitoring agents.</p>
|
||||
</div>
|
||||
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
|
||||
<div class="bg-surface-900 rounded-xl p-4 border border-surface-800">
|
||||
<div class="text-indigo-400 font-semibold mb-2">HPA</div>
|
||||
<p class="text-gray-400 text-sm">Horizontal Pod Autoscaler. Scale pods based on CPU/memory/custom metrics.</p>
|
||||
<p class="text-surface-400 text-sm">Horizontal Pod Autoscaler. Scale pods based on CPU/memory/custom metrics.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Networking -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl font-semibold text-white mb-4">Kubernetes Networking Model</h2>
|
||||
<h2 class="text-xl font-semibold text-surface-100 mb-4">Kubernetes Networking Model</h2>
|
||||
|
||||
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
|
||||
<div class="card">
|
||||
<div class="grid md:grid-cols-3 gap-6">
|
||||
<div class="p-4 bg-blue-500/10 rounded-lg border border-blue-500/30">
|
||||
<h4 class="text-blue-400 font-semibold mb-2">Pod-to-Pod</h4>
|
||||
<p class="text-gray-400 text-sm mb-2">Every pod gets a unique IP. Pods can communicate directly without NAT.</p>
|
||||
<div class="text-xs text-gray-500">Implemented by CNI plugin (Calico, Cilium, etc.)</div>
|
||||
<p class="text-surface-400 text-sm mb-2">Every pod gets a unique IP. Pods can communicate directly without NAT.</p>
|
||||
<div class="text-xs text-surface-500">Implemented by CNI plugin (Calico, Cilium, etc.)</div>
|
||||
</div>
|
||||
<div class="p-4 bg-green-500/10 rounded-lg border border-green-500/30">
|
||||
<h4 class="text-green-400 font-semibold mb-2">Pod-to-Service</h4>
|
||||
<p class="text-gray-400 text-sm mb-2">Services provide stable virtual IPs (ClusterIP). kube-proxy routes to pods.</p>
|
||||
<div class="text-xs text-gray-500">Uses iptables or IPVS rules</div>
|
||||
<p class="text-surface-400 text-sm mb-2">Services provide stable virtual IPs (ClusterIP). kube-proxy routes to pods.</p>
|
||||
<div class="text-xs text-surface-500">Uses iptables or IPVS rules</div>
|
||||
</div>
|
||||
<div class="p-4 bg-purple-500/10 rounded-lg border border-purple-500/30">
|
||||
<h4 class="text-purple-400 font-semibold mb-2">External-to-Service</h4>
|
||||
<p class="text-gray-400 text-sm mb-2">NodePort, LoadBalancer, or Ingress expose services externally.</p>
|
||||
<div class="text-xs text-gray-500">Ingress for HTTP, LoadBalancer for TCP/UDP</div>
|
||||
<p class="text-surface-400 text-sm mb-2">NodePort, LoadBalancer, or Ingress expose services externally.</p>
|
||||
<div class="text-xs text-surface-500">Ingress for HTTP, LoadBalancer for TCP/UDP</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -339,54 +356,54 @@
|
|||
|
||||
<!-- Common Patterns -->
|
||||
<section>
|
||||
<h2 class="text-xl font-semibold text-white mb-4">Production Best Practices</h2>
|
||||
<h2 class="text-xl font-semibold text-surface-100 mb-4">Production Best Practices</h2>
|
||||
|
||||
<div class="grid md:grid-cols-2 gap-6">
|
||||
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
|
||||
<div class="card">
|
||||
<h4 class="text-green-400 font-medium mb-3">DO</h4>
|
||||
<ul class="space-y-2">
|
||||
<li class="flex items-start gap-2 text-gray-300 text-sm">
|
||||
<li class="flex items-start gap-2 text-surface-300 text-sm">
|
||||
<span class="text-green-400">+</span>
|
||||
Set resource requests and limits on all pods
|
||||
</li>
|
||||
<li class="flex items-start gap-2 text-gray-300 text-sm">
|
||||
<li class="flex items-start gap-2 text-surface-300 text-sm">
|
||||
<span class="text-green-400">+</span>
|
||||
Use liveness and readiness probes
|
||||
</li>
|
||||
<li class="flex items-start gap-2 text-gray-300 text-sm">
|
||||
<li class="flex items-start gap-2 text-surface-300 text-sm">
|
||||
<span class="text-green-400">+</span>
|
||||
Run multiple replicas with anti-affinity
|
||||
</li>
|
||||
<li class="flex items-start gap-2 text-gray-300 text-sm">
|
||||
<li class="flex items-start gap-2 text-surface-300 text-sm">
|
||||
<span class="text-green-400">+</span>
|
||||
Use namespaces for isolation
|
||||
</li>
|
||||
<li class="flex items-start gap-2 text-gray-300 text-sm">
|
||||
<li class="flex items-start gap-2 text-surface-300 text-sm">
|
||||
<span class="text-green-400">+</span>
|
||||
Backup etcd regularly
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
|
||||
<div class="card">
|
||||
<h4 class="text-red-400 font-medium mb-3">DON'T</h4>
|
||||
<ul class="space-y-2">
|
||||
<li class="flex items-start gap-2 text-gray-300 text-sm">
|
||||
<li class="flex items-start gap-2 text-surface-300 text-sm">
|
||||
<span class="text-red-400">-</span>
|
||||
Run workloads on control plane nodes
|
||||
</li>
|
||||
<li class="flex items-start gap-2 text-gray-300 text-sm">
|
||||
<li class="flex items-start gap-2 text-surface-300 text-sm">
|
||||
<span class="text-red-400">-</span>
|
||||
Use :latest image tags in production
|
||||
</li>
|
||||
<li class="flex items-start gap-2 text-gray-300 text-sm">
|
||||
<li class="flex items-start gap-2 text-surface-300 text-sm">
|
||||
<span class="text-red-400">-</span>
|
||||
Store secrets in ConfigMaps
|
||||
</li>
|
||||
<li class="flex items-start gap-2 text-gray-300 text-sm">
|
||||
<li class="flex items-start gap-2 text-surface-300 text-sm">
|
||||
<span class="text-red-400">-</span>
|
||||
Skip pod disruption budgets
|
||||
</li>
|
||||
<li class="flex items-start gap-2 text-gray-300 text-sm">
|
||||
<li class="flex items-start gap-2 text-surface-300 text-sm">
|
||||
<span class="text-red-400">-</span>
|
||||
Ignore resource quotas in multi-tenant clusters
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -53,40 +53,46 @@
|
|||
title: 'Lambda Cold Start Flow',
|
||||
steps: [
|
||||
{
|
||||
id: 'cold-start-1',
|
||||
title: 'Invocation Request',
|
||||
content: 'API Gateway triggers Lambda function',
|
||||
description: 'API Gateway triggers Lambda function',
|
||||
details: 'A request arrives but no warm execution environment is available.',
|
||||
tips: ['Cold starts affect ~1% of requests', 'More common during traffic spikes']
|
||||
tip: 'Cold starts affect ~1% of requests. More common during traffic spikes.'
|
||||
},
|
||||
{
|
||||
id: 'cold-start-2',
|
||||
title: 'Container Provisioning',
|
||||
content: 'Lambda allocates compute resources',
|
||||
description: 'Lambda allocates compute resources',
|
||||
details: 'AWS provisions a micro-VM with the configured memory. More memory = more CPU.',
|
||||
tips: ['Memory range: 128MB - 10GB', 'CPU scales proportionally with memory']
|
||||
tip: 'Memory range: 128MB - 10GB. CPU scales proportionally with memory.'
|
||||
},
|
||||
{
|
||||
id: 'cold-start-3',
|
||||
title: 'Runtime Initialization',
|
||||
content: 'Load Python/Node.js/Java runtime',
|
||||
description: 'Load Python/Node.js/Java runtime',
|
||||
details: 'The language runtime is loaded and initialized in the container.',
|
||||
tips: ['Python/Node.js: ~100-200ms', 'Java: ~500ms-1s (JVM startup)']
|
||||
tip: 'Python/Node.js: ~100-200ms. Java: ~500ms-1s (JVM startup).'
|
||||
},
|
||||
{
|
||||
id: 'cold-start-4',
|
||||
title: 'Function Code Loading',
|
||||
content: 'Download and extract deployment package',
|
||||
description: 'Download and extract deployment package',
|
||||
details: 'Your code is downloaded from S3 and extracted. Larger packages take longer.',
|
||||
tips: ['Keep deployment package small', 'Use Lambda Layers for shared code']
|
||||
tip: 'Keep deployment package small. Use Lambda Layers for shared code.'
|
||||
},
|
||||
{
|
||||
id: 'cold-start-5',
|
||||
title: 'Static Initialization',
|
||||
content: 'Run code outside handler (global scope)',
|
||||
description: 'Run code outside handler (global scope)',
|
||||
details: 'Code at module level runs once per container. Initialize DB connections here.',
|
||||
tips: ['Initialize SDK clients outside handler', 'This code runs once per container lifecycle']
|
||||
tip: 'Initialize SDK clients outside handler. This code runs once per container lifecycle.'
|
||||
},
|
||||
{
|
||||
id: 'cold-start-6',
|
||||
title: 'Handler Execution',
|
||||
content: 'Your function code runs',
|
||||
description: 'Your function code runs',
|
||||
details: 'Finally, your handler function executes. Container is now "warm".',
|
||||
tips: ['Subsequent invocations skip steps 1-5', 'Warm container reused for ~15 minutes']
|
||||
tip: 'Subsequent invocations skip steps 1-5. Warm container reused for ~15 minutes.'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -159,40 +159,46 @@
|
|||
title: 'Cache-Aside Pattern Flow',
|
||||
steps: [
|
||||
{
|
||||
id: 'cache-aside-1',
|
||||
title: 'Application Requests Data',
|
||||
content: 'User requests user profile for user_id=123',
|
||||
description: 'User requests user profile for user_id=123',
|
||||
details: 'The application receives a request that needs data from the database.',
|
||||
tips: ['Common entry point: API endpoint, service method']
|
||||
tip: 'Common entry point: API endpoint, service method.'
|
||||
},
|
||||
{
|
||||
id: 'cache-aside-2',
|
||||
title: 'Check Cache First',
|
||||
content: 'GET user:123 from Redis',
|
||||
description: 'GET user:123 from Redis',
|
||||
details: 'Application checks if the data exists in cache before hitting the database.',
|
||||
tips: ['Use consistent key naming: entity:id', 'Handle cache connection failures gracefully']
|
||||
tip: 'Use consistent key naming: entity:id. Handle cache connection failures gracefully.'
|
||||
},
|
||||
{
|
||||
id: 'cache-aside-3',
|
||||
title: 'Cache Hit - Return Immediately',
|
||||
content: 'Data found! Return cached user profile',
|
||||
description: 'Data found! Return cached user profile',
|
||||
details: 'If data is in cache, return it immediately. This is the fast path.',
|
||||
tips: ['Typical cache hit latency: < 1ms', 'Log cache hits for monitoring']
|
||||
tip: 'Typical cache hit latency: < 1ms. Log cache hits for monitoring.'
|
||||
},
|
||||
{
|
||||
id: 'cache-aside-4',
|
||||
title: 'Cache Miss - Query Database',
|
||||
content: 'SELECT * FROM users WHERE id = 123',
|
||||
description: 'SELECT * FROM users WHERE id = 123',
|
||||
details: 'If cache miss, query the primary database for the data.',
|
||||
tips: ['This is the slow path', 'Consider query optimization']
|
||||
tip: 'This is the slow path. Consider query optimization.'
|
||||
},
|
||||
{
|
||||
id: 'cache-aside-5',
|
||||
title: 'Store in Cache',
|
||||
content: 'SET user:123 with TTL 3600 seconds',
|
||||
description: 'SET user:123 with TTL 3600 seconds',
|
||||
details: 'After getting data from DB, store it in cache for future requests.',
|
||||
tips: ['Always set a TTL to prevent stale data', 'Consider cache size limits']
|
||||
tip: 'Always set a TTL to prevent stale data. Consider cache size limits.'
|
||||
},
|
||||
{
|
||||
id: 'cache-aside-6',
|
||||
title: 'Return Data to Caller',
|
||||
content: 'Return user profile to client',
|
||||
description: 'Return user profile to client',
|
||||
details: 'Finally return the data. Next request will hit the cache.',
|
||||
tips: ['Monitor cache hit rate', 'Aim for > 90% hit rate']
|
||||
tip: 'Monitor cache hit rate. Aim for > 90% hit rate.'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -306,30 +306,30 @@
|
|||
<div class="space-y-3">
|
||||
<h4 class="text-sm font-medium text-surface-400 uppercase tracking-wide">By Use Case</h4>
|
||||
{#each [
|
||||
{ case: 'E-commerce orders', db: 'PostgreSQL', color: 'blue' },
|
||||
{ case: 'User profiles', db: 'MongoDB', color: 'green' },
|
||||
{ case: 'Session caching', db: 'Redis', color: 'red' },
|
||||
{ case: 'IoT sensor data', db: 'TimescaleDB', color: 'purple' },
|
||||
{ case: 'Social graph', db: 'Neo4j', color: 'pink' }
|
||||
{ case: 'E-commerce orders', db: 'PostgreSQL' },
|
||||
{ case: 'User profiles', db: 'MongoDB' },
|
||||
{ case: 'Session caching', db: 'Redis' },
|
||||
{ case: 'IoT sensor data', db: 'TimescaleDB' },
|
||||
{ case: 'Social graph', db: 'Neo4j' }
|
||||
] as item}
|
||||
<div class="flex items-center justify-between p-2 bg-surface-800 rounded-lg">
|
||||
<span class="text-sm text-surface-300">{item.case}</span>
|
||||
<span class="text-sm font-medium text-{item.color}-400">{item.db}</span>
|
||||
<span class="text-sm font-medium text-surface-200">{item.db}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<h4 class="text-sm font-medium text-surface-400 uppercase tracking-wide">By Data Type</h4>
|
||||
{#each [
|
||||
{ type: 'Relational', db: 'PostgreSQL/MySQL', color: 'blue' },
|
||||
{ type: 'Documents', db: 'MongoDB/Firestore', color: 'green' },
|
||||
{ type: 'Key-Value', db: 'Redis/DynamoDB', color: 'yellow' },
|
||||
{ type: 'Time-Series', db: 'TimescaleDB/InfluxDB', color: 'purple' },
|
||||
{ type: 'Graph', db: 'Neo4j/Neptune', color: 'pink' }
|
||||
{ type: 'Relational', db: 'PostgreSQL/MySQL' },
|
||||
{ type: 'Documents', db: 'MongoDB/Firestore' },
|
||||
{ type: 'Key-Value', db: 'Redis/DynamoDB' },
|
||||
{ type: 'Time-Series', db: 'TimescaleDB/InfluxDB' },
|
||||
{ type: 'Graph', db: 'Neo4j/Neptune' }
|
||||
] as item}
|
||||
<div class="flex items-center justify-between p-2 bg-surface-800 rounded-lg">
|
||||
<span class="text-sm text-surface-300">{item.type}</span>
|
||||
<span class="text-sm font-medium text-{item.color}-400">{item.db}</span>
|
||||
<span class="text-sm font-medium text-surface-200">{item.db}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
description: 'NOT possible in distributed systems',
|
||||
explanation: 'In a distributed system, network partitions are inevitable. When a partition occurs, you must choose between C and A.',
|
||||
examples: ['Single-node databases (PostgreSQL, MySQL on one server)', 'Not achievable in distributed systems'],
|
||||
useCase: '',
|
||||
color: '#94A3B8',
|
||||
viable: false
|
||||
},
|
||||
|
|
@ -110,7 +111,9 @@
|
|||
<g
|
||||
class="cursor-pointer"
|
||||
onclick={() => selectCombination('CA')}
|
||||
onmouseenter={() => {}}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onkeydown={(e) => (e.key === 'Enter' || e.key === ' ') && selectCombination('CA')}
|
||||
>
|
||||
<line x1={vertices.C.x} y1={vertices.C.y} x2={vertices.A.x} y2={vertices.A.y}
|
||||
stroke={selectedChoice === 'CA' ? '#94A3B8' : 'transparent'}
|
||||
|
|
@ -132,6 +135,9 @@
|
|||
<g
|
||||
class="cursor-pointer"
|
||||
onclick={() => selectCombination('CP')}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onkeydown={(e) => (e.key === 'Enter' || e.key === ' ') && selectCombination('CP')}
|
||||
>
|
||||
<line x1={vertices.C.x} y1={vertices.C.y} x2={vertices.P.x} y2={vertices.P.y}
|
||||
stroke={selectedChoice === 'CP' ? '#8B5CF6' : 'transparent'}
|
||||
|
|
@ -148,6 +154,9 @@
|
|||
<g
|
||||
class="cursor-pointer"
|
||||
onclick={() => selectCombination('AP')}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onkeydown={(e) => (e.key === 'Enter' || e.key === ' ') && selectCombination('AP')}
|
||||
>
|
||||
<line x1={vertices.A.x} y1={vertices.A.y} x2={vertices.P.x} y2={vertices.P.y}
|
||||
stroke={selectedChoice === 'AP' ? '#EC4899' : 'transparent'}
|
||||
|
|
@ -164,6 +173,7 @@
|
|||
{#each Object.entries(vertices) as [key, vertex]}
|
||||
<g
|
||||
class="cursor-pointer"
|
||||
role="presentation"
|
||||
onmouseenter={() => hoveredVertex = key as 'C' | 'A' | 'P'}
|
||||
onmouseleave={() => hoveredVertex = null}
|
||||
>
|
||||
|
|
@ -200,6 +210,7 @@
|
|||
{#each Object.entries(vertices) as [key, vertex]}
|
||||
<div
|
||||
class="p-4 rounded-lg border transition-all duration-200"
|
||||
role="presentation"
|
||||
class:bg-surface-800={hoveredVertex !== key}
|
||||
class:border-surface-700={hoveredVertex !== key}
|
||||
style:background-color={hoveredVertex === key ? `${vertex.color}20` : undefined}
|
||||
|
|
|
|||
|
|
@ -254,28 +254,25 @@
|
|||
{
|
||||
name: 'Cloudflare',
|
||||
icon: 'Cloud',
|
||||
color: 'orange',
|
||||
pros: ['Free tier', 'Fast propagation', 'DDoS protection'],
|
||||
bestFor: 'Most websites'
|
||||
},
|
||||
{
|
||||
name: 'Route 53',
|
||||
icon: 'Compass',
|
||||
color: 'yellow',
|
||||
pros: ['AWS integration', 'Health checks', 'Geo-routing'],
|
||||
bestFor: 'AWS infrastructure'
|
||||
},
|
||||
{
|
||||
name: 'Cloud DNS',
|
||||
icon: 'Cloud',
|
||||
color: 'blue',
|
||||
pros: ['GCP integration', '100% SLA', 'Global anycast'],
|
||||
bestFor: 'GCP infrastructure'
|
||||
}
|
||||
] as provider}
|
||||
<div class="bg-surface-800 rounded-lg p-4">
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<Icons.Cloud class="w-5 h-5 text-{provider.color}-400" />
|
||||
<Icons.Cloud class="w-5 h-5 text-surface-300" />
|
||||
<span class="font-medium text-surface-200">{provider.name}</span>
|
||||
</div>
|
||||
<ul class="space-y-1 mb-3">
|
||||
|
|
|
|||
|
|
@ -83,23 +83,23 @@
|
|||
</svelte:head>
|
||||
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<h1 class="text-3xl font-bold text-white mb-2">Load Balancing</h1>
|
||||
<p class="text-gray-400 mb-8">Understanding Layer 4 vs Layer 7 load balancers and when to use each</p>
|
||||
<h1 class="text-3xl font-bold text-surface-100 mb-2">Load Balancing</h1>
|
||||
<p class="text-surface-400 mb-8">Understanding Layer 4 vs Layer 7 load balancers and when to use each</p>
|
||||
|
||||
<!-- OSI Model Visual -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl font-semibold text-white mb-4">OSI Model & Load Balancer Layers</h2>
|
||||
<p class="text-gray-400 mb-6">Load balancers operate at different OSI layers, determining what information they can see and use for routing decisions.</p>
|
||||
<h2 class="text-xl font-semibold text-surface-100 mb-4">OSI Model & Load Balancer Layers</h2>
|
||||
<p class="text-surface-400 mb-6">Load balancers operate at different OSI layers, determining what information they can see and use for routing decisions.</p>
|
||||
|
||||
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
|
||||
<div class="card">
|
||||
<div class="grid gap-2">
|
||||
{#each osiLayers as layer}
|
||||
<div
|
||||
class="flex items-center gap-4 p-3 rounded-lg transition-all cursor-pointer
|
||||
class="flex items-center gap-4 p-3 rounded-lg transition-all cursor-pointer border border-surface-800 bg-surface-900
|
||||
{layer.num === 7 ? 'bg-blue-500/20 border border-blue-500/50' : ''}
|
||||
{layer.num === 4 ? 'bg-purple-500/20 border border-purple-500/50' : ''}
|
||||
{layer.num === 3 ? 'bg-orange-500/20 border border-orange-500/50' : ''}
|
||||
{layer.num !== 7 && layer.num !== 4 && layer.num !== 3 ? 'bg-gray-700/30' : ''}"
|
||||
"
|
||||
on:click={() => {
|
||||
if (layer.num === 7) selectedLayer = 'l7';
|
||||
else if (layer.num === 4) selectedLayer = 'l4';
|
||||
|
|
@ -112,12 +112,12 @@
|
|||
{layer.num === 7 ? 'bg-blue-500 text-white' : ''}
|
||||
{layer.num === 4 ? 'bg-purple-500 text-white' : ''}
|
||||
{layer.num === 3 ? 'bg-orange-500 text-white' : ''}
|
||||
{layer.num !== 7 && layer.num !== 4 && layer.num !== 3 ? 'bg-gray-600 text-gray-300' : ''}">
|
||||
{layer.num !== 7 && layer.num !== 4 && layer.num !== 3 ? 'bg-surface-800 text-surface-300' : ''}">
|
||||
{layer.num}
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<span class="text-white font-medium">{layer.name}</span>
|
||||
<span class="text-gray-500 ml-2">- {layer.protocols}</span>
|
||||
<span class="text-surface-100 font-medium">{layer.name}</span>
|
||||
<span class="text-surface-500 ml-2">- {layer.protocols}</span>
|
||||
</div>
|
||||
{#if layer.lb !== '-'}
|
||||
<span class="px-3 py-1 rounded-full text-sm font-medium
|
||||
|
|
@ -134,15 +134,15 @@
|
|||
<div class="mt-6 grid grid-cols-3 gap-4 text-center">
|
||||
<div class="p-3 rounded-lg bg-blue-500/10 border border-blue-500/30">
|
||||
<div class="text-blue-400 font-semibold">Layer 7 (ALB)</div>
|
||||
<div class="text-gray-400 text-sm mt-1">Sees HTTP content</div>
|
||||
<div class="text-surface-400 text-sm mt-1">Sees HTTP content</div>
|
||||
</div>
|
||||
<div class="p-3 rounded-lg bg-purple-500/10 border border-purple-500/30">
|
||||
<div class="text-purple-400 font-semibold">Layer 4 (NLB)</div>
|
||||
<div class="text-gray-400 text-sm mt-1">Sees IP + Port</div>
|
||||
<div class="text-surface-400 text-sm mt-1">Sees IP + Port</div>
|
||||
</div>
|
||||
<div class="p-3 rounded-lg bg-orange-500/10 border border-orange-500/30">
|
||||
<div class="text-orange-400 font-semibold">Layer 3 (GLB)</div>
|
||||
<div class="text-gray-400 text-sm mt-1">Network gateway</div>
|
||||
<div class="text-surface-400 text-sm mt-1">Network gateway</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -150,70 +150,70 @@
|
|||
|
||||
<!-- How They Work -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl font-semibold text-white mb-4">How They Work</h2>
|
||||
<h2 class="text-xl font-semibold text-surface-100 mb-4">How They Work</h2>
|
||||
|
||||
<div class="grid md:grid-cols-2 gap-6">
|
||||
<!-- L4 -->
|
||||
<div class="bg-gray-800/50 rounded-xl p-6 border border-purple-500/30">
|
||||
<div class="card border-purple-500/30">
|
||||
<h3 class="text-lg font-semibold text-purple-400 mb-4">Layer 4 Load Balancer</h3>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 rounded-full bg-purple-500/20 flex items-center justify-center text-purple-400 text-sm font-bold">1</div>
|
||||
<div>
|
||||
<div class="text-white font-medium">Extract 5-tuple</div>
|
||||
<div class="text-gray-400 text-sm">Source IP, Source Port, Dest IP, Dest Port, Protocol</div>
|
||||
<div class="text-surface-100 font-medium">Extract 5-tuple</div>
|
||||
<div class="text-surface-400 text-sm">Source IP, Source Port, Dest IP, Dest Port, Protocol</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 rounded-full bg-purple-500/20 flex items-center justify-center text-purple-400 text-sm font-bold">2</div>
|
||||
<div>
|
||||
<div class="text-white font-medium">Apply hash algorithm</div>
|
||||
<div class="text-gray-400 text-sm">Deterministic routing based on connection info</div>
|
||||
<div class="text-surface-100 font-medium">Apply hash algorithm</div>
|
||||
<div class="text-surface-400 text-sm">Deterministic routing based on connection info</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 rounded-full bg-purple-500/20 flex items-center justify-center text-purple-400 text-sm font-bold">3</div>
|
||||
<div>
|
||||
<div class="text-white font-medium">Forward entire connection</div>
|
||||
<div class="text-gray-400 text-sm">All packets in session go to same backend</div>
|
||||
<div class="text-surface-100 font-medium">Forward entire connection</div>
|
||||
<div class="text-surface-400 text-sm">All packets in session go to same backend</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 p-3 bg-purple-500/10 rounded-lg">
|
||||
<div class="text-purple-300 text-sm font-medium">Key insight:</div>
|
||||
<div class="text-gray-400 text-sm">Cannot see inside the packet payload - only network headers</div>
|
||||
<div class="text-surface-400 text-sm">Cannot see inside the packet payload - only network headers</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- L7 -->
|
||||
<div class="bg-gray-800/50 rounded-xl p-6 border border-blue-500/30">
|
||||
<div class="card border-blue-500/30">
|
||||
<h3 class="text-lg font-semibold text-blue-400 mb-4">Layer 7 Load Balancer</h3>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 rounded-full bg-blue-500/20 flex items-center justify-center text-blue-400 text-sm font-bold">1</div>
|
||||
<div>
|
||||
<div class="text-white font-medium">Terminate connection</div>
|
||||
<div class="text-gray-400 text-sm">Client connects to LB, LB connects to backend</div>
|
||||
<div class="text-surface-100 font-medium">Terminate connection</div>
|
||||
<div class="text-surface-400 text-sm">Client connects to LB, LB connects to backend</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 rounded-full bg-blue-500/20 flex items-center justify-center text-blue-400 text-sm font-bold">2</div>
|
||||
<div>
|
||||
<div class="text-white font-medium">Parse HTTP request</div>
|
||||
<div class="text-gray-400 text-sm">Read URL path, headers, cookies, body</div>
|
||||
<div class="text-surface-100 font-medium">Parse HTTP request</div>
|
||||
<div class="text-surface-400 text-sm">Read URL path, headers, cookies, body</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 rounded-full bg-blue-500/20 flex items-center justify-center text-blue-400 text-sm font-bold">3</div>
|
||||
<div>
|
||||
<div class="text-white font-medium">Apply routing rules</div>
|
||||
<div class="text-gray-400 text-sm">/api/* to API servers, /static/* to CDN</div>
|
||||
<div class="text-surface-100 font-medium">Apply routing rules</div>
|
||||
<div class="text-surface-400 text-sm">/api/* to API servers, /static/* to CDN</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 p-3 bg-blue-500/10 rounded-lg">
|
||||
<div class="text-blue-300 text-sm font-medium">Key insight:</div>
|
||||
<div class="text-gray-400 text-sm">Full visibility into application data - can route by content</div>
|
||||
<div class="text-surface-400 text-sm">Full visibility into application data - can route by content</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -221,23 +221,23 @@
|
|||
|
||||
<!-- Feature Comparison Table -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl font-semibold text-white mb-4">Feature Comparison</h2>
|
||||
<h2 class="text-xl font-semibold text-surface-100 mb-4">Feature Comparison</h2>
|
||||
|
||||
<div class="bg-gray-800/50 rounded-xl border border-gray-700 overflow-hidden">
|
||||
<div class="bg-surface-900 rounded-xl border border-surface-800 overflow-hidden">
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
<tr class="border-b border-gray-700">
|
||||
<th class="text-left p-4 text-gray-400 font-medium">Feature</th>
|
||||
<tr class="border-b border-surface-800">
|
||||
<th class="text-left p-4 text-surface-400 font-medium">Feature</th>
|
||||
<th class="text-left p-4 text-purple-400 font-medium">Layer 4 (NLB)</th>
|
||||
<th class="text-left p-4 text-blue-400 font-medium">Layer 7 (ALB)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each features as feature, i}
|
||||
<tr class="border-b border-gray-700/50 {i % 2 === 0 ? 'bg-gray-800/30' : ''}">
|
||||
<td class="p-4 text-white font-medium">{feature.name}</td>
|
||||
<td class="p-4 text-gray-300">{feature.l4}</td>
|
||||
<td class="p-4 text-gray-300">{feature.l7}</td>
|
||||
<tr class="border-b border-surface-800 {i % 2 === 0 ? 'bg-surface-950/40' : ''}">
|
||||
<td class="p-4 text-surface-100 font-medium">{feature.name}</td>
|
||||
<td class="p-4 text-surface-300">{feature.l4}</td>
|
||||
<td class="p-4 text-surface-300">{feature.l7}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
|
|
@ -247,28 +247,28 @@
|
|||
|
||||
<!-- When to Use -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl font-semibold text-white mb-4">When to Use Each</h2>
|
||||
<h2 class="text-xl font-semibold text-surface-100 mb-4">When to Use Each</h2>
|
||||
<ComparisonTable options={comparisonOptions} />
|
||||
</section>
|
||||
|
||||
<!-- AWS Services -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl font-semibold text-white mb-4">AWS Load Balancer Types</h2>
|
||||
<h2 class="text-xl font-semibold text-surface-100 mb-4">AWS Load Balancer Types</h2>
|
||||
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{#each awsServices as service}
|
||||
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700 hover:border-gray-500 transition-colors">
|
||||
<div class="bg-surface-900 rounded-xl p-4 border border-surface-800 hover:border-surface-700 transition-colors">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span class="text-lg font-bold text-white">{service.name}</span>
|
||||
<span class="text-lg font-bold text-surface-100">{service.name}</span>
|
||||
<span class="px-2 py-0.5 rounded text-xs font-medium
|
||||
{service.layer === 'L7' ? 'bg-blue-500/20 text-blue-400' : ''}
|
||||
{service.layer === 'L4' ? 'bg-purple-500/20 text-purple-400' : ''}
|
||||
{service.layer === 'L3/L4' ? 'bg-orange-500/20 text-orange-400' : ''}
|
||||
{service.layer === 'L4/L7' ? 'bg-gray-500/20 text-gray-400' : ''}">
|
||||
{service.layer === 'L4/L7' ? 'bg-surface-700/40 text-surface-300' : ''}">
|
||||
{service.layer}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-gray-400 text-sm">{service.use}</p>
|
||||
<p class="text-surface-400 text-sm">{service.use}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
@ -276,47 +276,47 @@
|
|||
|
||||
<!-- Decision Guide -->
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl font-semibold text-white mb-4">Quick Decision Guide</h2>
|
||||
<h2 class="text-xl font-semibold text-surface-100 mb-4">Quick Decision Guide</h2>
|
||||
|
||||
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
|
||||
<div class="card">
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center gap-4 p-4 bg-purple-500/10 rounded-lg border border-purple-500/30">
|
||||
<div class="text-2xl">1M+</div>
|
||||
<div>
|
||||
<div class="text-white font-medium">Need millions of requests/second?</div>
|
||||
<div class="text-gray-400 text-sm">Use NLB (Layer 4) - designed for extreme performance</div>
|
||||
<div class="text-surface-100 font-medium">Need millions of requests/second?</div>
|
||||
<div class="text-surface-400 text-sm">Use NLB (Layer 4) - designed for extreme performance</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 p-4 bg-blue-500/10 rounded-lg border border-blue-500/30">
|
||||
<div class="text-2xl">/api</div>
|
||||
<div>
|
||||
<div class="text-white font-medium">Need path-based routing?</div>
|
||||
<div class="text-gray-400 text-sm">Use ALB (Layer 7) - can route by URL, headers, cookies</div>
|
||||
<div class="text-surface-100 font-medium">Need path-based routing?</div>
|
||||
<div class="text-surface-400 text-sm">Use ALB (Layer 7) - can route by URL, headers, cookies</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 p-4 bg-purple-500/10 rounded-lg border border-purple-500/30">
|
||||
<div class="text-2xl">IP</div>
|
||||
<div>
|
||||
<div class="text-white font-medium">Need static IP addresses?</div>
|
||||
<div class="text-gray-400 text-sm">Use NLB (Layer 4) - provides static IPs per AZ</div>
|
||||
<div class="text-surface-100 font-medium">Need static IP addresses?</div>
|
||||
<div class="text-surface-400 text-sm">Use NLB (Layer 4) - provides static IPs per AZ</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 p-4 bg-blue-500/10 rounded-lg border border-blue-500/30">
|
||||
<div class="text-2xl">WAF</div>
|
||||
<div>
|
||||
<div class="text-white font-medium">Need WAF or advanced security?</div>
|
||||
<div class="text-gray-400 text-sm">Use ALB (Layer 7) - integrates with AWS WAF</div>
|
||||
<div class="text-surface-100 font-medium">Need WAF or advanced security?</div>
|
||||
<div class="text-surface-400 text-sm">Use ALB (Layer 7) - integrates with AWS WAF</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 p-4 bg-gray-700/50 rounded-lg border border-gray-600">
|
||||
<div class="flex items-center gap-4 p-4 bg-surface-800/60 rounded-lg border border-surface-800">
|
||||
<div class="text-2xl">DB</div>
|
||||
<div>
|
||||
<div class="text-white font-medium">Load balancing databases or non-HTTP?</div>
|
||||
<div class="text-gray-400 text-sm">Use NLB (Layer 4) - protocol agnostic</div>
|
||||
<div class="text-surface-100 font-medium">Load balancing databases or non-HTTP?</div>
|
||||
<div class="text-surface-400 text-sm">Use NLB (Layer 4) - protocol agnostic</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -325,41 +325,41 @@
|
|||
|
||||
<!-- Real World Examples -->
|
||||
<section>
|
||||
<h2 class="text-xl font-semibold text-white mb-4">Real World Examples</h2>
|
||||
<h2 class="text-xl font-semibold text-surface-100 mb-4">Real World Examples</h2>
|
||||
|
||||
<div class="grid md:grid-cols-2 gap-6">
|
||||
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
|
||||
<h3 class="text-lg font-semibold text-white mb-3">E-commerce Platform</h3>
|
||||
<div class="card">
|
||||
<h3 class="text-lg font-semibold text-surface-100 mb-3">E-commerce Platform</h3>
|
||||
<div class="space-y-2 text-sm">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="px-2 py-0.5 rounded bg-blue-500/20 text-blue-400">ALB</span>
|
||||
<span class="text-gray-300">/api/* to API servers</span>
|
||||
<span class="text-surface-300">/api/* to API servers</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="px-2 py-0.5 rounded bg-blue-500/20 text-blue-400">ALB</span>
|
||||
<span class="text-gray-300">/static/* to static content servers</span>
|
||||
<span class="text-surface-300">/static/* to static content servers</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="px-2 py-0.5 rounded bg-blue-500/20 text-blue-400">ALB</span>
|
||||
<span class="text-gray-300">Sticky sessions for shopping cart</span>
|
||||
<span class="text-surface-300">Sticky sessions for shopping cart</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
|
||||
<h3 class="text-lg font-semibold text-white mb-3">Gaming Server</h3>
|
||||
<div class="card">
|
||||
<h3 class="text-lg font-semibold text-surface-100 mb-3">Gaming Server</h3>
|
||||
<div class="space-y-2 text-sm">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="px-2 py-0.5 rounded bg-purple-500/20 text-purple-400">NLB</span>
|
||||
<span class="text-gray-300">UDP game traffic (low latency)</span>
|
||||
<span class="text-surface-300">UDP game traffic (low latency)</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="px-2 py-0.5 rounded bg-purple-500/20 text-purple-400">NLB</span>
|
||||
<span class="text-gray-300">TCP matchmaking servers</span>
|
||||
<span class="text-surface-300">TCP matchmaking servers</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="px-2 py-0.5 rounded bg-purple-500/20 text-purple-400">NLB</span>
|
||||
<span class="text-gray-300">Static IPs for firewall rules</span>
|
||||
<span class="text-surface-300">Static IPs for firewall rules</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue