SiamCafe · Blog
StencilJS Pod Scheduling — การจัดการ Web
บทความ

StencilJS Pod Scheduling — การจัดการ Web

เผยแพร่ 28 พฤษภาคม 2569

StencilJS Web Components

StencilJS Compiler สร้าง Web Components มาตรฐาน TypeScript JSX Ionic Team Custom Elements ทำงานทุก Framework React Angular Vue Vanilla JS Lazy Loading Virtual DOM SSR

FeatureStencilJSLitSvelte
LanguageTypeScript + JSXTypeScriptSvelte Syntax
OutputWeb ComponentsWeb ComponentsVanilla JS
Bundle Sizeเล็ก (Tree-shaking)เล็กมาก (5KB)เล็กมาก
SSRรองรับรองรับSvelteKit
Lazy LoadingBuilt-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