StencilJS Pod Scheduling — การจัดการ Web
StencilJS Web Components
StencilJS Compiler สร้าง Web Components มาตรฐาน TypeScript JSX Ionic Team Custom Elements ทำงานทุก Framework React Angular Vue Vanilla JS Lazy Loading Virtual DOM SSR
| Feature | StencilJS | Lit | Svelte |
|---|---|---|---|
| Language | TypeScript + JSX | TypeScript | Svelte Syntax |
| Output | Web Components | Web Components | Vanilla JS |
| Bundle Size | เล็ก (Tree-shaking) | เล็กมาก (5KB) | เล็กมาก |
| SSR | รองรับ | รองรับ | SvelteKit |
| Lazy Loading | Built-in | ไม่มี | ไม่มี |
StencilJS Component
=== StencilJS Setup & Component ===
npm init stencil
Choose: component (for a component library)
cd my-components
npm install
npm start
src/components/my-counter/my-counter.tsx
import { Component, State, h } from '@stencil/core';
@Component({
tag: 'my-counter',
styleUrl: 'my-counter.css',
shadow: true,
})
export class MyCounter {
@State() count: number = 0;
private increment = () => { this.count++; };
private decrement = () => { this.count--; };
render() {
return (
<div class="counter">
<button onClick={this.decrement}>-</button>
<span>{this.count}</span>
<button onClick={this.increment}>+</button>
</div>
);
}
}
src/components/my-counter/my-counter.css
.counter {
display: flex;
align-items: center;
gap: 12px;
font-family: sans-serif;
}
button {
padding: 8px 16px;
border: none;
border-radius: 4px;
background: #3880ff;
color: white;
cursor: pointer;
}
span {
font-size: 24px;
min-width: 40px;
text-align: center;
}
stencil.config.ts
import { Config } from '@stencil/core';
export const config: Config = {
namespace: 'my-components',
outputTargets: [
{ type: 'dist', esmLoaderPath: '../loader' },
{ type: 'dist-custom-elements' },
{ type: 'www', serviceWorker: null },
],
};
Build & Publish
npm run build
npm publish
Usage in HTML
<script type="module" src="my-components/dist/my-components.esm.js"></script>
<my-counter></my-counter>
Usage in React
import { MyCounter } from 'my-components-react';
function App() {
return <MyCounter />;
}
from dataclasses import dataclass
from typing import List
@dataclass
class WebComponent:
tag: str
props: List[str]
events: List[str]
slots: List[str]
shadow: bool
components = [
WebComponent("my-counter", ["initial-count"], ["countChanged"], [], True),
WebComponent("my-button", ["variant", "size", "disabled"], ["click"], ["default"], True),
WebComponent("my-modal", ["open", "title"], ["close", "confirm"], ["default", "footer"], True),
WebComponent("my-toast", ["message", "type", "duration"], ["dismiss"], [], True),
]
print("StencilJS Component Library:")
for c in components:
print(f"\n <{c.tag}>")
print(f" Props: {', '.join(c.props)}")
print(f" Events: {', '.join(c.events)}")
print(f" Shadow DOM: {c.shadow}")
Kubernetes Pod Scheduling
=== Kubernetes Pod Scheduling ===
1. nodeSelector — วิธีง่ายสุด
apiVersion: v1
kind: Pod
metadata:
name: frontend-pod
spec:
nodeSelector:
disk: ssd
env: production
containers:
- name: app
image: my-stencil-app:latest
2. Node Affinity — ยืดหยุ่นกว่า
apiVersion: v1
kind: Pod
metadata:
name: frontend-pod
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values: [amd64]
- key: node-type
operator: In
values: [frontend, general]
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: zone
operator: In
values: [ap-southeast-1a]
containers:
- name: app
image: my-stencil-app:latest
3. Taints & Tolerations
kubectl taint nodes node1 dedicated=frontend:NoSchedule
kubectl taint nodes gpu-node nvidia.com/gpu=true:NoSchedule
apiVersion: v1
kind: Pod
spec:
tolerations:
- key: "dedicated"
operator: "Equal"
value: "frontend"
effect: "NoSchedule"
containers:
- name: app
image: my-stencil-app:latest
4. Pod Anti-Affinity — กระจาย Pod
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values: [frontend]
topologyKey: kubernetes.io/hostname
5. Topology Spread Constraints
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: frontend
6. Priority Classes
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "High priority for critical services"
scheduling_strategies = {
"nodeSelector": {
"use_case": "เลือก Node ตาม Label แบบง่าย",
"flexibility": "Low — Hard Match เท่านั้น",
"example": "nodeSelector: {disk: ssd}",
},
"Node Affinity": {
"use_case": "เลือก Node ด้วย Expression ซับซ้อน",
"flexibility": "High — Hard + Soft Match",
"example": "requiredDuring + preferredDuring",
},
"Taints/Tolerations": {
"use_case": "ไล่ Pod ออกจาก Node ที่ไม่ต้องการ",
"flexibility": "Medium — Node-centric",
"example": "dedicated=gpu:NoSchedule",
},
"Pod Anti-Affinity": {
"use_case": "กระจาย Pod ไม่ให้อยู่ Node เดียวกัน",
"flexibility": "High — HA Setup",
"example": "topologyKey: hostname",
},
"Topology Spread": {
"use_case": "กระจาย Pod ตาม Zone/Region",
"flexibility": "High — Multi-zone",
"example": "maxSkew: 1, zone",
},
}
print("Kubernetes Pod Scheduling Strategies:")
for strategy, info in scheduling_strategies.items():
print(f"\n [{strategy}]")
for k, v in info.items():
print(f" {k}: {v}")
CI/CD Deploy
=== StencilJS CI/CD with K8s Scheduling ===
.github/workflows/deploy.yml
name: Deploy StencilJS App
on:
push:
branches: [main]
jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run build
- run: npm test
- name: Build Docker Image
run: |
docker build -t my-registry/stencil-app:} .
docker push my-registry/stencil-app:}
- name: Deploy to K8s
run: |
kubectl set image deployment/frontend \
app=my-registry/stencil-app:}
kubectl rollout status deployment/frontend
Dockerfile
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/www /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
K8s Deployment with Scheduling
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 3
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-type
operator: In
values: [frontend, general]
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: frontend
topologyKey: kubernetes.io/hostname
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: frontend
containers:
- name: app
image: my-registry/stencil-app:latest
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
deploy_checklist = {
"Build": ["npm ci", "npm run build", "npm test", "docker build"],
"Push": ["docker push to registry", "tag with git SHA"],
"Deploy": ["kubectl set image", "rollout status", "health check"],
"Scheduling": ["Node Affinity", "Pod Anti-Affinity", "Topology Spread"],
"Monitor": ["kubectl top pods", "Prometheus metrics", "Grafana dashboard"],
}
print("Deployment Checklist:")
for phase, steps in deploy_checklist.items():
print(f"\n [{phase}]")
for step in steps:
print(f" - {step}")
เคล็ดลับ
- StencilJS: ใช้สร้าง Design System ที่ทำงานทุก Framework
- Node Affinity: ใช้แทน nodeSelector ยืดหยุ่นกว่า
- Pod Anti-Affinity: กระจาย Pod ไม่ให้อยู่ Node เดียวกันสำหรับ HA
- Topology Spread: กระจาย Pod ตาม Zone สำหรับ Multi-AZ
- Priority: ใช้ PriorityClass สำหรับ Critical Services
StencilJS คืออะไร
Compiler สร้าง Web Components TypeScript JSX Ionic Custom Elements ทำงานทุก Framework React Angular Vue Lazy Loading SSR