نسخه آنلاین در حال بارگذاری زمان... تهران: ۲۶°C
۲۸ کاربر آنلاین

PNo.30Light

نشریه تخصصی هوش مصنوعی، سیستم‌های سرور و مهندسی داده

تازه ترین‌ها
زیرساخت و سرور
زمان مطالعه: ۳۴ دقیقه ۰ بازدید

spec در YAML کوبرنتیز: Deep Dive وضعیت مطلوب — PodSpec، Workloads و الگوهای مشترک

نویسنده: تحریریه فنی P30Light
spec در YAML کوبرنتیز: Deep Dive وضعیت مطلوب — PodSpec، Workloads و الگوهای مشترک
✦ خلاصه نکات کلیدی مقاله
  • spec = desired state که شما اعلام می‌کنید؛ control plane با status واقعی reconcile می‌کند.
  • شکل spec برای هر Kind فرق دارد؛ هسته مشترک بسیاری workloadها Pod template است.
  • بعضی فیلدهای spec بعد از create قفل‌اند (مثل selector)؛ تغییر template معمولاً rollout می‌سازد.

چهار ستون یک مانیفست کلاسیک:

apiVersion: apps/v1          # قرارداد API
kind: Deployment             # نوع شیء
metadata:                    # هویت — مقاله جدا
  name: web
spec:                        # وضعیت مطلوب ← موضوع این مقاله
  replicas: 3
  selector: { ... }
  template: { ... }
# status:                    # وضعیت مشاهده‌شده — سیستم می‌نویسد

طبق مستندات اشیاء Kubernetes: تقریباً هر شیء دارای spec (آنچه می‌خواهید) و status (آنچه واقعاً هست) است. control plane پیوسته actual را به desired نزدیک می‌کند.

این مقاله deep dive روی spec است — نه تکرار مقاله metadata. هویت در metadata است؛ قصد اجرایی در spec.


spec در برابر metadata در برابر status

لایهنویسنده اصلینقش
metadataکاربر + سیستمنام، ns، labels، owners، finalizers، resourceVersion
specکاربر (و گاه admission/defaulting)desired state — «باید این‌گونه باشد»
statusسیستم / کنترلرهاobserved state — Ready، replicas، conditions، IP
شما: spec.replicas = 3
کنترلر: می‌بیند status.readyReplicas = 2 → یک Pod دیگر می‌سازد

kubectl edit روی status معمولاً بی‌فایده یا موقتی است؛ منبع حقیقت برای intent همان spec (و Git در GitOps) است.


قانون طلایی: هر Kind، یک شکل spec

هیچ «لیست ثابت جهانی فیلدهای spec» برای همه منابع وجود ندارد. spec یک Pod با spec یک Service یا Certificate (CRD) کاملاً فرق دارد.

kubectl explain pod.spec
kubectl explain deployment.spec
kubectl explain service.spec
kubectl explain ingress.spec --api-version=networking.k8s.io/v1

الگوهای تکرارشونده:

  1. مستقیم: منابعی که خودشان اجرا می‌شوند (Pod، تا حدی Job)
  2. Controller + template: Deployment / StatefulSet / DaemonSet / CronJob → داخل spec یک Pod template
  3. شبکه / سیاست: Service، Ingress، NetworkPolicy — بدون container
  4. پیکربندی: بخش زیادی از intent در data است نه همیشه زیر کلید spec (ConfigMap/Secret)
  5. CRD: هر اپراتور schema خودش را در spec تعریف می‌کند

نقشه ذهنی: از Deployment تا Container

Deployment.spec
├── replicas
├── selector                 (اغلب immutable)
├── strategy                 (RollingUpdate / Recreate)
├── revisionHistoryLimit
├── minReadySeconds
└── template                 # PodTemplateSpec
    ├── metadata.labels      # باید با selector جور باشد
    └── spec                 # ← این همان PodSpec است
        ├── containers[]
        ├── initContainers[]
        ├── volumes[]
        ├── restartPolicy
        ├── nodeSelector / affinity / tolerations
        ├── securityContext
        ├── serviceAccountName
        ├── dnsPolicy / hostNetwork / ...
        └── ...

بیشتر «عمق spec» که روزمره می‌نویسید، در PodSpec (یا template.spec) است.


Deep Dive: PodSpec — هستهٔ اجرا

Containers (الزامی برای Pod مفید)

هر container حداقل name و معمولاً image دارد:

spec:
  containers:
    - name: api
      image: ghcr.io/example/api:1.4.2
      imagePullPolicy: IfNotPresent   # Always | Never | IfNotPresent
      command: ["api"]                # Entrypoint override
      args: ["--port=8080"]
      ports:
        - name: http
          containerPort: 8080
          protocol: TCP
      env:
        - name: ENV
          value: production
        - name: DB_URL
          valueFrom:
            secretKeyRef:
              name: db
              key: url
      envFrom:
        - configMapRef:
            name: api-config
      resources:
        requests:
          cpu: "100m"
          memory: "128Mi"
        limits:
          cpu: "500m"
          memory: "512Mi"
      volumeMounts:
        - name: data
          mountPath: /var/lib/api
      workingDir: /app
فیلدنقش
nameیکتا داخل Pod
imageایمیج اجرا
imagePullPolicyAlways برای :latest پیش‌فرض رایج است
command / argsجایگزین ENTRYPOINT/CMD ایمیج
portsمستندسازی + شبکه؛ باز کردن port روی Service جداست
env / envFromمتغیر محیطی
resourcesscheduling (requests) و سقف (limits)
volumeMountsاتصال volume تعریف‌شده در Pod

Probes — تشخیص سلامت

Kubelet دوره‌ای چک می‌کند:

Probeمعنی عملی
livenessProbeمرده؟ → restart container
readinessProbeآماده ترافیک؟ → حذف از Endpoints سرویس
startupProbeشروع کند؛ تا موفق نشود liveness را عقب می‌اندازد

انواع action: httpGet، tcpSocket، exec، grpc.

readinessProbe:
  httpGet:
    path: /healthz
    port: http
  initialDelaySeconds: 5
  periodSeconds: 10
  timeoutSeconds: 2
  failureThreshold: 3

بدون readiness درست، Service ترافیک را به Pod نیمه‌آماده می‌فرستد.

Lifecycle hooks

lifecycle:
  postStart:
    exec:
      command: ["/bin/sh", "-c", "echo start"]
  preStop:
    exec:
      command: ["/bin/sh", "-c", "sleep 5"]  # فرصت drain

همراه terminationGracePeriodSeconds در سطح Pod برای SIGTERM → SIGKILL.

initContainers و sidecars

spec:
  initContainers:
    - name: migrate
      image: migrate:1
      command: ["migrate", "up"]
  containers:
    - name: api
      image: api:1

Initها ترتیبی و تا موفقیت کامل قبل از app containers اجرا می‌شوند. الگوی sidecar (مثلاً log/proxy) container هم‌عمر در همان Pod است؛ در نسخه‌های جدیدتر Kubernetes مفهوم sidecar با restartPolicy ویژه روی init هم مطرح شده — با kubectl explain نسخه cluster چک کنید.

volumes و volumeMounts

تعریف در spec.volumes، مصرف در container:

نوع رایجکاربرد
emptyDirموقت هم‌عمر Pod
configMap / secretفایل پیکربندی
persistentVolumeClaimداده پایدار
projectedترکیب چند منبع
csi / hostPath / nfsوابسته به محیط
spec:
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: api-data
  containers:
    - name: api
      volumeMounts:
        - name: data
          mountPath: /data

Scheduling: کجا اجرا شود؟

فیلدنقش
nodeSelectorساده: label نود
affinity / antiAffinityقوانین نرم/سخت نسبت به نود یا Pod
tolerationsتحمل taint نود
topologySpreadConstraintsپخش بین zone/host
runtimeClassNameمثلاً Kata — مقاله Kata
priorityClassNameاولویت scheduling / preemption
schedulerNamescheduler غیرپیش‌فرض

امنیت در spec

سطح Pod (spec.securityContext) و سطح container (containers[].securityContext):

spec:
  serviceAccountName: api-sa
  automountServiceAccountToken: false
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: api
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: ["ALL"]

تنظیمات container در صورت تداخل، تنظیمات هم‌پوشان Pod را override می‌کنند (جزئیات volume ownership جداست).

شبکه و DNS داخل PodSpec

فیلدنقش
hostname / subdomainDNS داخلی
dnsPolicyClusterFirst، Default، …
dnsConfignameserver/search سفارشی — مرتبط با CoreDNS
hostNetwork / hostPID / hostIPCاشتراک namespace با نود (حساس امنیتی)
shareProcessNamespacePID مشترک بین containers

restartPolicy و مهلت‌ها

مقدارکاربرد
Alwaysپیش‌فرض Deployment/RS
OnFailureJobهای رایج
NeverJob دقیق / debug
spec:
  restartPolicy: Always
  terminationGracePeriodSeconds: 30
  activeDeadlineSeconds: 600   # سقف عمر (مثلاً Job/Pod)

فیلدهایی که سیستم روی Pod پر می‌کند (نه در مانیفست اولیه شما)

بعد از schedule، مواردی مثل nodeName، و در status آدرس‌ها ظاهر می‌شوند. ویرایش دستی بسیاری از فیلدهای Pod ممنوع یا محدود است.


محدودیت تغییر Pod بعد از create

طبق مستندات Pod، به‌روزرسانی in-place Pod عمدتاً محدود است به چیزهایی مثل:

  • spec.containers[*].image / initContainers[*].image
  • spec.activeDeadlineSeconds
  • spec.terminationGracePeriodSeconds
  • افزودن به spec.tolerations
  • و برخی فیلدهای جدیدتر مثل scheduling gates

عوض کردن ports، env، volumes، یا افزودن container → معمولاً رد یا نیاز به حذف/بازساخت. به همین دلیل workload controllers با عوض کردن template Pod جدید می‌سازند.


Workload specs: کنترلرها

Deployment (apps/v1)

فیلد specنقش
replicasتعداد مطلوب Pod
selectorکدام Podها مال این Deploymentاند — در apps/v1 بعد از create immutable
templatePodTemplateSpec
strategy.typeRollingUpdate یا Recreate
strategy.rollingUpdatemaxUnavailable / maxSurge
revisionHistoryLimitنگهداشت ReplicaSetهای قدیمی
progressDeadlineSecondsکی rollout شکست‌خورده اعلام شود
pausedتوقف rollout

قانون حیاتی:

spec.selector باید با template.metadata.labels جور باشد
وگرنه API رد می‌کند

تغییر template (image، label، env، …) → ReplicaSet جدید و rollout. تغییر صرف replicas scale است نه لزوماً revision کامل image.

ReplicaSet

معمولاً مستقیم نمی‌نویسید؛ زیر Deployment ساخته می‌شود. spec مشابه: replicas + selector + template.

StatefulSet

برای هویت پایدار و storage:

فیلدنقش
serviceNameheadless Service برای DNS پایدار
volumeClaimTemplatesPVC per Pod
podManagementPolicyOrderedReady / Parallel
updateStrategyRollingUpdate / OnDelete
ordinalsکنترل شماره ایندکس (نسخه‌های جدیدتر)

نام Podها پایدار: web-0، web-1، …

DaemonSet

یک (یا بیشتر با tolerance) Pod per Node مطابق selector. spec.updateStrategy برای rolling روی نودها.

Job و CronJob (batch/v1)

Job.spec:

فیلدنقش
templatePod؛ معمولاً restartPolicy: OnFailure/Never
completions / parallelismچند کار موفق / موازی
backoffLimitسقف retry
ttlSecondsAfterFinishedپاکسازی خودکار
activeDeadlineSecondsمهلت کل Job

CronJob.spec: schedule، jobTemplate، concurrencyPolicy (Allow/Forbid/Replace)، successfulJobsHistoryLimit.


شبکه در spec (بدون Pod template)

Service

spec:
  type: ClusterIP          # NodePort | LoadBalancer | ExternalName
  selector:
    app: web               # label روی Podها — نه لزوماً روی Deployment
  ports:
    - name: http
      port: 80
      targetPort: http
      protocol: TCP
  sessionAffinity: None
  # clusterIP / externalIPs / loadBalancerIP — وابسته به محیط

بدون selector می‌توان Endpoints/EndpointSlice دستی داشت (ExternalName یا سرویس headless پیشرفته).

Ingress (networking.k8s.io/v1)

spec:
  ingressClassName: nginx
  tls:
    - hosts: ["app.example.com"]
      secretName: app-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

pathType اجباری است؛ جزئیات apiVersion در مقاله apiVersion.

NetworkPolicy

spec.podSelector + ingress/egress rules — کدام ترافیک مجاز است (Cilium اغلب موتور enforcement است).


Config و Secret — intent کجا نشسته؟

Kindالگوی رایج
ConfigMap / Secretعمدتاً data / stringData؛ گاهی immutable: true
PersistentVolumeClaimspec.accessModes، resources.requests.storage، storageClassName
HorizontalPodAutoscalerspec.scaleTargetRef، minReplicas، maxReplicas، metrics

Secret در etcd (رمزنگاری در rest وابسته به پیکربندی cluster) — در Git از Sealed/ESO استفاده کنید.


Defaulting، Validation و Apply

  1. شما YAML ناقص می‌فرستید
  2. Defaulting فیلدهای پیش‌فرض را پر می‌کند (مثلاً protocol: TCP)
  3. Validation (OpenAPI / CEL) رد یا قبول می‌کند
  4. Admission webhooks ممکن است spec را mutate/validate کنند
  5. شیء ذخیره می‌شود؛ کنترلر reconcile را شروع می‌کند

از Kubernetes 1.25+ field validation سمت سرور قوی‌تر است (kubectl --validate=strict).

kubectl apply -f app.yaml --server-side   # SSA؛ مرتبط با managedFields در metadata
kubectl diff -f app.yaml
kubectl explain deploy.spec.strategy.rollingUpdate.maxSurge

Immutable در برابر Mutable در spec

معمولاً بعد از create قفلمعمولاً قابل تغییر
Deployment spec.selectorreplicas، template، strategy
بسیاری label selectorهای مالکیتimage داخل template (با rollout)
Service clusterIP (پس از تخصیص)ports (با محدودیت)، selector
Job selector در برخی حالاتparallelism در محدوده مجاز
PVC storageClassName / حجم در بسیاری provisionerهاannotationها؛ افزایش حجم اگر CSI اجازه دهد

قانون عملی: اگر API گفت immutable → object جدید بسازید یا از کنترلر بخواهید جایگزین کند (rollout).


CRD و Operators: spec سفارشی

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: app-tls
spec:
  secretName: app-tls
  issuerRef:
    name: letsencrypt
    kind: ClusterIssuer
  dnsNames:
    - app.example.com

اینجا spec دیگر PodSpec نیست — schema اپراتور است. همان اصل برقرار است: شما desired می‌نویسید؛ کنترلر status را پر می‌کند و عمل می‌کند. مثال‌ها: cert-manager، Istio VirtualService، Flux HelmRelease، Helm values که به spec تبدیل می‌شوند.


نمونهٔ یکپارچه حاشیه‌نویسی‌شده

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: production
  labels:
    app.kubernetes.io/name: web
spec:
  replicas: 3                          # desired count
  revisionHistoryLimit: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  selector:                            # immutable پس از create
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web                       # باید با selector یکی باشد
    spec:                              # PodSpec
      serviceAccountName: web
      securityContext:
        runAsNonRoot: true
      containers:
        - name: web
          image: ghcr.io/example/web:1.2.0
          ports:
            - containerPort: 8080
              name: http
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              memory: 256Mi
          readinessProbe:
            httpGet:
              path: /ready
              port: http
          livenessProbe:
            httpGet:
              path: /live
              port: http

اشتباهات رایج درباره spec

اشتباهواقعیت
ویرایش Pod زنده به‌جای Deploymentdrift؛ کنترلر برمی‌گرداند یا فیلد immutable است
selector سرویس = labelهای metadata دیپلویمنتباید label Pod باشد
نداشتن readinessترافیک به Pod سرد
requests خیلی کم / بدون limit حافظهنویز همسایه یا OOM بی‌برنامه
فرض اینکه همه Kindها replicas دارندService/Ingress ندارند
commit کردن status در Gitintent نیست؛ نویز reconcile
عوض کردن selector Deploymentمعمولاً باید recreate

ارتباط با GitOps و Helm

  • Helm: values → رندر → پر کردن specها
  • Flux / Argo: Git را desired می‌دانند و spec cluster را هم‌تراز می‌کنند
  • تغییر spec در cluster بدون Git → drift تا reconcile بعدی

سه‌گانه مقالات مرتبط:

  1. apiVersion — قرارداد نوع
  2. metadata — هویت و روابط
  3. spec (این مقاله) — وضعیت مطلوب

Best practices

  1. همیشه با kubectl explain <kind>.spec نسخه cluster خودتان را مرجع کنید
  2. intent را در Git نگه دارید؛ status را ignore کنید
  3. probes را جدی بگیرید — readiness برای Service حیاتی است
  4. requests را برای scheduling واقعی بگذارید
  5. securityContext حداقلی (non-root، drop caps) را پیش‌فرض کنید
  6. selectorها را از روز اول پایدار طراحی کنید
  7. برای داده پایدار StatefulSet/PVC؛ برای stateless Deployment
  8. قبل از apply: kubectl diff و validate
  9. در CI، schema را با kubeconform روی version هدف تست کنید
  10. CRD: فقط فیلدهای documented اپراتور را در spec بنویسید

جمع‌بندی

مفهومیک جمله
specاعلام desired state توسط شما
statusگزارش actual state توسط سیستم
PodSpecهستهٔ اجرا داخل اکثر workloadها
templateقالب ساخت Pod توسط کنترلر
immutable fieldsبعد از create قفل؛ مسیر تغییر = recreate/rollout
هر Kindشکل spec مخصوص خود را دارد

spec جایی است که می‌گویید cluster چه باید بکند. metadata می‌گوید شیء کیست؛ status می‌گوید اکنون کجاست. تسلط بر PodSpec و الگوی controller+template، خواندن تقریباً هر YAML کوبرنتیز را ممکن می‌کند.


قدم بعدی

  1. kubectl explain pod.spec --recursive | less را یک‌بار ورق بزنید
  2. یک Deployment را با kubectl get deploy -o yaml باز کنید و فقط بلوک spec را مطالعه کنید
  3. readiness را عمداً بشکنید و ببینید Endpoints خالی می‌شود
  4. سعی کنید spec.selector یک Deployment را عوض کنید — خطای immutable را ببینید
  5. مقالات metadata و apiVersion را در کنار این یکی کامل کنید

منابع


منتشر شده در P30Light — بخش زیرساخت و سرور.

لینک گزارش با موفقیت کپی گردید!