کوبرنتیز را معمولاً با حجم زیادی YAML میشناسند، ولی وقتی منطق پشت آن روشن شود، بیشتر مانیفستها تکرار همان سه الگو هستند: Deployment (برنامه را اجرا کن)، Service (آدرس ثابتی برایش بساز) و Ingress (از بیرون به آن راه بده). در این راهنما یک اپلیکیشن وب را با این سه میچینیم و برای هر فیلد توضیح میدهیم چرا آنجاست.
K8SKubernetes YAML Generatorاگر میخواهید قبل از خواندن، خروجی را ببینید: Deployment، Service و Ingress را با فرم بسازید.Deployment: برنامه را اجرا و زنده نگه دار
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web # باید با labels داخل template یکی باشد
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: registry.example.com/web:1.8.2 # تگ ثابت، نه latest
ports:
- containerPort: 8080
env:
- name: LOG_LEVEL
value: info
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { cpu: 500m, memory: 256Mi }
readinessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 15
periodSeconds: 10
برچسبها و selector؛ چسب کل سیستم
Deployment، Pod و Service هیچ پیوند مستقیمی ندارند؛ همه با برچسب (label) به هم وصل میشوند. spec.selector.matchLabels میگوید «من مسئول Podهایی هستم که این برچسب را دارند» و باید دقیقاً با template.metadata.labels یکی باشد؛ در غیر این صورت API مانیفست را رد میکند. همان برچسب را Service هم در selector خود به کار میبرد.
requests و limits
requests مقداری است که scheduler برای جاگذاری Pod روی یک نود در نظر میگیرد؛ limits سقف مصرف است. اگر مصرف حافظه از limit بیشتر شود کانتینر OOMKilled میشود؛ مصرف CPU بالاتر از limit فقط کند (throttle) میشود. اگر requests را ننویسید، Pod میتواند روی نودی بنشیند که جا ندارد و کل نود را ناپایدار کند.
readiness و liveness؛ دو سؤال متفاوت
- readinessProbe: «آیا همین الان میتوانم ترافیک بگیرم؟» اگر شکست بخورد، Pod از Service کنار گذاشته میشود ولی ریاستارت نمیشود.
- livenessProbe: «آیا گیر کردهام؟» اگر شکست بخورد، کانتینر ریاستارت میشود.
بهروزرسانی تدریجی
با maxSurge: 1 و maxUnavailable: 0 قبل از حذف هر Pod قدیمی یک Pod جدید ساخته و آماده میشود؛ یعنی استقرار بدون قطعی (به شرط وجود readinessProbe درست).
Service: آدرس ثابت برای Podهای متغیر
Podها میمیرند و IP عوض میشود. Service یک IP و نام DNS ثابت میدهد و ترافیک را میان Podهای آماده تقسیم میکند.
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: ClusterIP
selector:
app: web # Podهایی که این برچسب را دارند
ports:
- name: http
port: 80 # پورتی که Service نشان میدهد
targetPort: 8080 # پورت کانتینر
درون کلاستر، سایر برنامهها با http://web (یا web.<namespace>.svc.cluster.local) به آن میرسند. نوع ClusterIP پیشفرض و برای پشت Ingress ایدهآل است.
Ingress: ورود از بیرون
Ingress قواعد مسیریابی HTTP را میگوید؛ ولی خودش کاری نمیکند مگر یک Ingress Controller (مثل ingress-nginx) در کلاستر نصب باشد.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
ingressClassName: nginx
tls:
- hosts: [example.com]
secretName: example-com-tls
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
مقدار pathType: Prefix یعنی همهی مسیرهایی که با / شروع میشوند. secretName به یک Secret از نوع kubernetes.io/tls اشاره میکند که گواهی و کلید را دارد (معمولاً با cert-manager ساخته میشود).
اعمال و بررسی
kubectl apply -f web.yaml
kubectl rollout status deployment/web
kubectl get pods -l app=web -o wide
kubectl describe pod <pod-name> # بخش Events دلیل اکثر مشکلها را میگوید
kubectl logs deploy/web --tail=100
kubectl get svc,ingress
قبل از apply بهتر است ساختار YAML را بررسی کنید؛ یک تورفتگی اشتباه بیشتر از هر چیز دیگری وقت میگیرد. برای پیشنمایش تغییرات روی کلاستر زنده از kubectl diff -f web.yaml هم میتوانید استفاده کنید.
پیامهای خطای رایج و معنی آنها
| وضعیت | معنی | اول چه ببینید |
|---|---|---|
| ImagePullBackOff | دانلود ایمیج شکست خورده | نام و تگ ایمیج؛ imagePullSecrets برای رجیستری خصوصی؛ دسترسی نود به رجیستری (روی سرور ایران، آینهی رجیستری) |
| CrashLoopBackOff | برنامه بالا میآید و میمیرد | kubectl logs --previous |
| Pending | جایی برای اجرا پیدا نشده | Events: منابع نود، PVC، taint |
| OOMKilled | حافظه از limit بیشتر شد | limits.memory و نشتی حافظه |
| 0 endpoints | Service به Podی وصل نیست | selector و برچسب Pod؛ readiness |
قدمهای بعدی
- HPA (HorizontalPodAutoscaler): تعداد replicas را بر اساس CPU/حافظه خودکار تغییر میدهد؛ حتماً
requestsداشته باشید که معیار محاسبه است. - ConfigMap و Secret: تنظیمات را از ایمیج جدا کنید و با
envFromیا volume بخوانید. - PodDisruptionBudget: هنگام نگهداری نودها جلوی قطع همزمان همهی نمونهها را بگیرد.
- Namespace و RBAC: محیطها و دسترسیها را جدا کنید.
همهی این منابع (Deployment تا HPA و PVC) در سازندهی مانیفست آمادهاند. و اگر هنوز کلاستر ندارید، راهنمای نصب kubeadm، kubelet و kubectl روی سرور ایران را از مخزن داخلی ببینید؛ نسخهها و دستورها هم روی صفحهی Kubernetes در dacker.ir آمده است.
جمعبندی و مطالعهی بعدی
مانیفستهای پایه (Deployment، Service و Ingress) پایهی هر استقرار هستند. برای مرحلهی بعد، تنظیم requests، limits و HPA پایداری و مقیاسپذیری را تأمین میکند، مدیریت ConfigMap و Secret تنظیمات و رمزها را از ایمیج جدا میکند و راهنمای عیبیابی با kubectl وقتی پادها بالا نمیآیند مسیر بررسی را نشان میدهد.
پرسشهای متداول
تفاوت Deployment و Pod چیست؟
ClusterIP، NodePort و LoadBalancer چه فرقی دارند؟
چرا Pod من در حالت Pending میماند؟
requests) پیدا نکرده، یا PVC بسته نشده، یا taint و nodeSelector مانع شدهاند. kubectl describe pod در بخش Events دلیل را مینویسد.