Files
Deploy-Laboratory/scripts/gen-nodejs-demo-yaml.py
2026-03-27 16:58:41 +08:00

479 lines
21 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Generate cumulative 04-02..04-11 nodejs-demo YAML (Core→Plus→Pro doc order)."""
from pathlib import Path
from textwrap import dedent
DIR = Path(__file__).resolve().parents[1] / "labs/nodejs/manifests"
CM = dedent(
"""\
apiVersion: v1 # ConfigMap API 版本
kind: ConfigMap # 配置资源ConfigMap
metadata: # ConfigMap 元信息
name: nodejs-demo-config # ConfigMap 名称
namespace: default # 命名空间
data: # 配置键值
APP_MSG: "Hello from ConfigMap" # 注入给应用的消息内容
"""
).strip()
SVC_8080 = dedent(
"""\
apiVersion: v1 # Service API 版本
kind: Service # Service 资源
metadata: # Service 元信息
name: nodejs-demo # Service 名称
namespace: default # 命名空间
spec: # Service 规格
selector: # 选择后端 Pod
app: nodejs-demo # 选中 app=nodejs-demo
ports: # 端口映射
- port: 80 # Service 暴露端口
targetPort: 8080 # 转发到容器端口
"""
).strip()
ING_NODE = dedent(
"""\
apiVersion: networking.k8s.io/v1 # Ingress API 版本
kind: Ingress # Ingress 资源
metadata: # Ingress 元信息
name: nodejs-demo # Ingress 名称
namespace: default # 命名空间
annotations: # Traefik 注解
traefik.ingress.kubernetes.io/router.entrypoints: web # 使用 web(HTTP) 入口
spec: # Ingress 规则
rules: # 规则列表
- http: # HTTP 路由
paths: # 路径列表
- path: /node # 匹配路径前缀
pathType: Prefix # 前缀匹配
backend: # 后端目标
service: # 后端 Service
name: nodejs-demo # Service 名称
port: # Service 端口
number: 80 # 端口号
"""
).strip()
ING_HOST = dedent(
"""\
apiVersion: networking.k8s.io/v1 # Ingress API 版本
kind: Ingress # Ingress 资源
metadata: # Ingress 元信息
name: nodejs-demo # Ingress 名称
namespace: default # 命名空间
annotations: # Traefik 注解
traefik.ingress.kubernetes.io/router.entrypoints: web # 使用 web(HTTP) 入口
spec: # Ingress 规则
rules: # 规则列表
- host: app.example.local # 主机名匹配
http: # HTTP 路由
paths: # 路径列表
- path: /api # 匹配 API 路径前缀
pathType: Prefix # 前缀匹配
backend: # 后端目标
service: # 后端 Service
name: nodejs-demo # Service 名称
port: # Service 端口
number: 80 # 端口号
"""
).strip()
# 与 Deployment 模板中 ` ports:` 同级(勿对整段 dedent否则会剥掉缩进
PROBES = (
" livenessProbe: # 存活探针\n"
" httpGet: # HTTP 探测\n"
" path: / # 探测路径\n"
" port: 8080 # 探测端口\n"
" initialDelaySeconds: 3 # 初始延迟\n"
" periodSeconds: 10 # 探测周期\n"
" readinessProbe: # 就绪探针\n"
" httpGet: # HTTP 探测\n"
" path: / # 探测路径\n"
" port: 8080 # 探测端口\n"
" initialDelaySeconds: 2 # 初始延迟\n"
" periodSeconds: 5 # 探测周期\n"
)
RES = (
" resources: # 资源请求与限制\n"
" requests: # 最小资源请求\n"
" cpu: \"50m\" # 请求 CPU\n"
" memory: \"64Mi\" # 请求内存\n"
" limits: # 资源上限\n"
" cpu: \"500m\" # CPU 限制\n"
" memory: \"256Mi\" # 内存限制\n"
)
def main() -> None:
# 04-02: 01 + 仅改监听 8080无 ConfigMap
doc2 = dedent(
"""\
# 对应文档docs/04-02-nodejs-端口与Service.md
# 累积04-01 + 容器与 Service 改监听 8080与后续探针一致
apiVersion: apps/v1 # Deployment API 版本
kind: Deployment # 工作负载Deployment
metadata: # Deployment 元信息
name: nodejs-demo # Deployment 名称
namespace: default # 命名空间
spec: # Deployment 规格
replicas: 1 # 副本数
selector: # Deployment 选择器
matchLabels: # 标签匹配集合
app: nodejs-demo # 匹配 app=nodejs-demo 的 Pod
template: # Pod 模板
metadata: # Pod 元信息
labels: # Pod 标签
app: nodejs-demo # 与 selector.matchLabels 对齐
spec: # Pod 规格
containers: # 容器列表
- name: nodejs-demo # 容器名
image: node:18-alpine # Node.js 镜像
command: ["node", "-e", "require('http').createServer((req,res)=>res.end('Hello World from Node.js')).listen(8080)"] # 内联 HTTP 服务改监听 8080
ports: # 容器端口
- containerPort: 8080 # 应用监听端口
---
"""
) + SVC_8080 + "\n---\n" + ING_NODE + "\n"
# 04-03: + 固定镜像 tag、command/args与旧 04-02 等价,端口 8080
doc3 = dedent(
"""\
# 对应文档docs/04-03-nodejs-镜像与运行命令.md
# 累积04-02 + 固定镜像 tag、imagePullPolicy、command/args
apiVersion: apps/v1 # Deployment API 版本
kind: Deployment # 工作负载Deployment
metadata: # Deployment 元信息
name: nodejs-demo # Deployment 名称
namespace: default # 命名空间
spec: # Deployment 规格
replicas: 1 # 副本数
selector: # Deployment 选择器
matchLabels: # 标签匹配集合
app: nodejs-demo # 匹配 app=nodejs-demo 的 Pod
template: # Pod 模板
metadata: # Pod 元信息
labels: # Pod 标签
app: nodejs-demo # 与 selector.matchLabels 对齐
spec: # Pod 规格
containers: # 容器列表
- name: nodejs-demo # 容器名
image: node:18.20-alpine # 固定 tag 的 Node.js 镜像
imagePullPolicy: IfNotPresent # 拉取策略:本地有则不重复拉取
command: ["node"] # 主命令
args: # 命令参数
- "-e" # 执行内联脚本
- "require('http').createServer((req,res)=>res.end('Hello from pinned image')).listen(8080)" # Node.js 内联服务逻辑
ports: # 容器端口
- containerPort: 8080 # 应用监听端口
---
"""
) + SVC_8080 + "\n---\n" + ING_NODE + "\n"
# 04-04: + ConfigMap等同旧 04-04 主体)
doc4 = (
f"# 对应文档docs/04-04-nodejs-环境变量与配置注入.md\n"
f"# 累积04-03 + ConfigMap + 通过 env 注入 APP_MSG\n---\n{CM}\n---\n"
+ dedent(
"""\
apiVersion: apps/v1 # Deployment API 版本
kind: Deployment # 工作负载Deployment
metadata: # Deployment 元信息
name: nodejs-demo # Deployment 名称
namespace: default # 命名空间
spec: # Deployment 规格
replicas: 1 # 副本数
selector: # Deployment 选择器
matchLabels: # 标签匹配集合
app: nodejs-demo # 匹配 app=nodejs-demo 的 Pod
template: # Pod 模板
metadata: # Pod 元信息
labels: # Pod 标签
app: nodejs-demo # 与 selector.matchLabels 对齐
spec: # Pod 规格
containers: # 容器列表
- name: nodejs-demo # 容器名
image: node:18.20-alpine # Node.js 镜像
imagePullPolicy: IfNotPresent # 拉取策略
env: # 环境变量注入
- name: APP_MSG # 环境变量名
valueFrom: # 从资源引用取值
configMapKeyRef: # 从 ConfigMap key 读取
name: nodejs-demo-config # ConfigMap 名称
key: APP_MSG # ConfigMap 键名
command: # 启动命令
- node # 运行 node
- "-e" # 执行内联脚本
- | # 多行 JS 脚本(内部内容不改动)
const http=require('http');
const msg=process.env.APP_MSG||'no env';
http.createServer((q,s)=>s.end(msg)).listen(8080);
ports: # 容器端口
- containerPort: 8080 # 应用监听端口
---
"""
)
+ SVC_8080
+ "\n---\n"
+ ING_NODE
+ "\n"
)
# 04-05: + 探针(无 resources
doc5 = (
f"# 对应文档docs/04-05-nodejs-探针与健康检查.md\n"
f"# 累积04-04 + livenessProbe/readinessProbe端口 8080路径 /\n---\n{CM}\n---\n"
+ dedent(
"""\
apiVersion: apps/v1 # Deployment API 版本
kind: Deployment # 工作负载Deployment
metadata: # Deployment 元信息
name: nodejs-demo # Deployment 名称
namespace: default # 命名空间
spec: # Deployment 规格
replicas: 1 # 副本数
selector: # Deployment 选择器
matchLabels: # 标签匹配集合
app: nodejs-demo # 匹配 app=nodejs-demo 的 Pod
template: # Pod 模板
metadata: # Pod 元信息
labels: # Pod 标签
app: nodejs-demo # 与 selector.matchLabels 对齐
spec: # Pod 规格
containers: # 容器列表
- name: nodejs-demo # 容器名
image: node:18.20-alpine # Node.js 镜像
imagePullPolicy: IfNotPresent # 拉取策略
env: # 环境变量注入
- name: APP_MSG # 环境变量名
valueFrom: # 从资源引用取值
configMapKeyRef: # 从 ConfigMap key 读取
name: nodejs-demo-config # ConfigMap 名称
key: APP_MSG # ConfigMap 键名
command: # 启动命令
- node # 运行 node
- "-e" # 执行内联脚本
- | # 多行 JS 脚本(内部内容不改动)
const http=require('http');
const msg=process.env.APP_MSG||'no env';
http.createServer((q,s)=>s.end(msg)).listen(8080);
ports: # 容器端口
- containerPort: 8080 # 应用监听端口
"""
).rstrip()
+ "\n"
+ PROBES
+ "\n"
+ dedent(
"""\
---
"""
)
+ SVC_8080
+ "\n---\n"
+ ING_NODE
+ "\n"
)
# 04-06: + replicas:3 + RollingUpdateIngress 仍为 /node
doc6 = (
f"# 对应文档docs/04-06-nodejs-副本与滚动发布.md\n"
f"# 累积04-05 + replicas: 3 + RollingUpdatemaxSurge:1 maxUnavailable:0\n---\n{CM}\n---\n"
+ dedent(
"""\
apiVersion: apps/v1 # Deployment API 版本
kind: Deployment # 工作负载Deployment
metadata: # Deployment 元信息
name: nodejs-demo # Deployment 名称
namespace: default # 命名空间
spec: # Deployment 规格
replicas: 3 # 副本数(高可用)
strategy: # 更新策略
type: RollingUpdate # 滚动更新
rollingUpdate: # 滚动更新参数
maxSurge: 1 # 更新时最多额外增加 1 个 Pod
maxUnavailable: 0 # 更新时不可用 Pod 数为 0
selector: # Pod 选择器
matchLabels: # 标签匹配集合
app: nodejs-demo # 匹配 app=nodejs-demo 的 Pod
template: # Pod 模板
metadata: # Pod 元信息
labels: # Pod 标签
app: nodejs-demo # 与 selector.matchLabels 对齐
spec: # Pod 规格
containers: # 容器列表
- name: nodejs-demo # 容器名
image: node:18.20-alpine # Node.js 镜像
imagePullPolicy: IfNotPresent # 拉取策略
env: # 环境变量注入
- name: APP_MSG # 环境变量名
valueFrom: # 从资源引用取值
configMapKeyRef: # 从 ConfigMap key 读取
name: nodejs-demo-config # ConfigMap 名称
key: APP_MSG # ConfigMap 键名
command: # 启动命令
- node # 运行 node
- "-e" # 执行内联脚本
- | # 多行 JS 脚本(内部内容不改动)
const http=require('http');
const msg=process.env.APP_MSG||'no env';
http.createServer((q,s)=>s.end(msg)).listen(8080);
ports: # 容器端口
- containerPort: 8080 # 应用监听端口
"""
).rstrip()
+ "\n"
+ PROBES
+ "\n"
+ dedent(
"""\
---
"""
)
+ SVC_8080
+ "\n---\n"
+ ING_NODE
+ "\n"
)
# 04-07: Ingress host + /api
doc7 = doc6.replace(
"# 对应文档docs/04-06-nodejs-副本与滚动发布.md\n"
"# 累积04-05 + replicas: 3 + RollingUpdatemaxSurge:1 maxUnavailable:0\n",
"# 对应文档docs/04-07-nodejs-Ingress与Traefik.md\n"
"# 累积04-06 + Ingress 增加 host、path 改为 /api访问需 Host: app.example.local\n",
)
doc7 = doc7.replace("---\n" + ING_NODE + "\n", "---\n" + ING_HOST + "\n")
# 04-08: + resources
c8 = (
" ports: # 容器端口\n"
" - containerPort: 8080 # 应用监听端口\n"
)
c8r = (
" ports: # 容器端口\n"
" - containerPort: 8080 # 应用监听端口\n" + RES
)
doc8 = doc7.replace(
"# 对应文档docs/04-07-nodejs-Ingress与Traefik.md\n"
"# 累积04-06 + Ingress 增加 host、path 改为 /api访问需 Host: app.example.local\n",
"# 对应文档docs/04-08-nodejs-资源请求与限制.md\n"
"# 累积04-07 + resources.requests/limits\n",
).replace(c8, c8r)
# 04-09: + nodeSelector
doc9 = doc8.replace(
"# 对应文档docs/04-08-nodejs-资源请求与限制.md\n"
"# 累积04-07 + resources.requests/limits\n",
"# 对应文档docs/04-09-nodejs-调度与亲和.md\n"
"# 累积04-08 + nodeSelector默认 ylc62请改为本集群节点短主机名\n",
).replace(
" spec: # Pod 规格\n containers: # 容器列表\n",
" spec: # Pod 规格\n nodeSelector: # 调度到指定节点\n"
" kubernetes.io/hostname: ylc62 # 节点主机名(按实际修改)\n"
" containers: # 容器列表\n",
)
# 04-10: + securityContext + tmp volume
doc10 = doc9.replace(
"# 对应文档docs/04-09-nodejs-调度与亲和.md\n"
"# 累积04-08 + nodeSelector默认 ylc62请改为本集群节点短主机名\n",
"# 对应文档docs/04-10-nodejs-安全上下文.md\n"
"# 累积04-09 + pod securityContext.fsGroup、容器 securityContext、只读根、/tmp emptyDir\n",
).replace(
" spec: # Pod 规格\n nodeSelector: # 调度到指定节点\n"
" kubernetes.io/hostname: ylc62 # 节点主机名(按实际修改)\n"
" containers: # 容器列表\n",
" spec: # Pod 规格\n nodeSelector: # 调度到指定节点\n"
" kubernetes.io/hostname: ylc62 # 节点主机名(按实际修改)\n"
" securityContext: # Pod 级安全上下文\n"
" fsGroup: 1000 # 挂载卷文件组 ID\n"
" containers: # 容器列表\n",
)
doc10 = doc10.replace(
" - name: nodejs-demo # 容器名\n image: node:18.20-alpine # Node.js 镜像\n"
" imagePullPolicy: IfNotPresent # 拉取策略\n env:",
" - name: nodejs-demo # 容器名\n image: node:18.20-alpine # Node.js 镜像\n"
" imagePullPolicy: IfNotPresent # 拉取策略\n"
" securityContext: # 容器级安全上下文\n"
" allowPrivilegeEscalation: false # 禁止提权\n"
" runAsNonRoot: true # 强制非 root 运行\n"
" runAsUser: 1000 # 运行用户 UID\n"
" readOnlyRootFilesystem: true # 根文件系统只读\n"
" env:",
)
doc10 = doc10.replace(
" periodSeconds: 5 # 探测周期\n\n---\n",
" periodSeconds: 5 # 探测周期\n"
" volumeMounts: # 卷挂载\n"
" - name: tmp # 引用临时卷\n"
" mountPath: /tmp # 容器内临时目录\n"
" volumes: # 卷定义\n"
" - name: tmp # 临时卷名称\n"
" emptyDir: {} # 空目录卷Pod 生命周期内)\n\n---\n",
)
pvc = dedent(
"""\
apiVersion: v1 # PVC API 版本
kind: PersistentVolumeClaim # 持久卷声明
metadata: # PVC 元信息
name: nodejs-demo-data # PVC 名称
namespace: default # 命名空间
spec: # PVC 规格
accessModes: # 访问模式
- ReadWriteOnce # RWO同一时间仅单节点挂载读写
storageClassName: local-path # 存储类(按集群可改)
resources: # 资源请求
requests: # 配额请求
storage: 1Gi # 申请容量
---
"""
).strip()
doc11 = doc10.replace(
"# 对应文档docs/04-10-nodejs-安全上下文.md\n"
"# 累积04-09 + pod securityContext.fsGroup、容器 securityContext、只读根、/tmp emptyDir\n",
"# 对应文档docs/04-11-nodejs-存储与卷.md\n"
"# 累积04-10 + PVC nodejs-demo-data默认 storageClassName: local-path+ 挂载 /data\n",
)
doc11 = doc11.replace(
"---\n" + CM + "\n---\n",
"---\n" + pvc + "\n" + CM + "\n---\n",
1,
)
doc11 = doc11.replace(
" volumeMounts: # 卷挂载\n"
" - name: tmp # 引用临时卷\n"
" mountPath: /tmp # 容器内临时目录\n",
" volumeMounts: # 卷挂载\n"
" - name: tmp # 临时卷名称\n"
" mountPath: /tmp # 容器内临时目录\n"
" - name: data # 数据卷名称\n"
" mountPath: /data # 容器内数据目录\n",
)
doc11 = doc11.replace(
" volumes: # 卷定义\n - name: tmp # 临时卷名称\n"
" emptyDir: {} # 空目录卷Pod 生命周期内)\n",
" volumes: # 卷定义\n - name: tmp # 临时卷\n emptyDir: {} # 空目录卷\n"
" - name: data # 数据卷\n persistentVolumeClaim: # 卷来源为 PVC\n"
" claimName: nodejs-demo-data # 绑定 PVC 名称\n",
)
DIR.mkdir(parents=True, exist_ok=True)
(DIR / "04-02-nodejs-demo.yaml").write_text(doc2, encoding="utf-8")
(DIR / "04-03-nodejs-demo.yaml").write_text(doc3, encoding="utf-8")
(DIR / "04-04-nodejs-demo.yaml").write_text(doc4, encoding="utf-8")
(DIR / "04-05-nodejs-demo.yaml").write_text(doc5, encoding="utf-8")
(DIR / "04-06-nodejs-demo.yaml").write_text(doc6, encoding="utf-8")
(DIR / "04-07-nodejs-demo.yaml").write_text(doc7, encoding="utf-8")
(DIR / "04-08-nodejs-demo.yaml").write_text(doc8, encoding="utf-8")
(DIR / "04-09-nodejs-demo.yaml").write_text(doc9, encoding="utf-8")
(DIR / "04-10-nodejs-demo.yaml").write_text(doc10, encoding="utf-8")
(DIR / "04-11-nodejs-demo.yaml").write_text(doc11, encoding="utf-8")
if __name__ == "__main__":
main()