Helm وتغليف Kubernetes

التعمّق في نظام القوالب

18 دقيقة الدرس 4 من 28

التعمّق في نظام القوالب

القوة الحقيقية لـ Helm تكمن في نظام قوالبه. كل ملف داخل templates/ يُعالَج عبر حزمة text/template في لغة Go، مُعزَّزةً بمكتبة Sprig للدوال إضافةً إلى الدوال المدمجة في Helm. فهم هذا المحرّك بعمق يُفرّق بين من يكتب Charts هشّة ومن يكتب charts قابلة لإعادة الاستخدام وجاهزة للإنتاج. يتناول هذا الدرس نموذج التنفيذ الكامل: كيف تتدفق القيم، وكيف تُحوّلها الدوال والخطوط الأنبوبية، وكيف تُتيح التعابير الشرطية والحلقات لقالب واحد التكيّف مع كل بيئة.

سياق تنفيذ القالب

كل قالب Helm يُنفَّذ ضمن كائن جذر يُسمّى . (النقطة). هذا الكائن هو مزيج من عدة فضاءات أسماء يملؤها Helm قبل بدء التصيير:

  • .Values — الناتج المدمج من values.yaml بالإضافة إلى كل ملف --values وكل علامة --set، بترتيب الأولوية (الأخير يكسب).
  • .Chart — بيانات التعريف من Chart.yaml: .Chart.Name و.Chart.Version و.Chart.AppVersion.
  • .Release — حالة الإصدار عند التشغيل: .Release.Name و.Release.Namespace و.Release.IsInstall و.Release.IsUpgrade.
  • .Capabilities — استطلاع الكلاستر: .Capabilities.KubeVersion.Minor و.Capabilities.APIVersions.Has "networking.k8s.io/v1".
  • .Files — الوصول إلى الملفات غير القوالب المضمّنة في الـ chart (ملفات الإعداد والشهادات).
Helm template execution context values.yaml + --values + --set Chart.yaml name, version, appVersion Release Context name, namespace, revision Capabilities KubeVersion, API groups . السياق الجذر .Values .Chart .Release Go Template Engine + Sprig دوال، خطوط أنبوبية شروط، حلقات YAML manifests
يدمج Helm جميع المدخلات في سياق جذر واحد (النقطة) ويمرّره عبر محرّك قوالب Go لإنتاج Kubernetes manifests مُصيَّرة.
تحذير تغيير النطاق: عند الدخول في حلقة range أو كتلة with، تُعاد تعيين النقطة . للعنصر أو القيمة الحالية. للوصول إلى اسم الإصدار الخارجي داخل الحلقة، استخدم $.Release.Name — فالبادئة $ تشير دائماً إلى الكائن الجذر بصرف النظر عن النطاق.

القيم — الواجهة العامة للـ Chart

values.yaml ليس مجرد ملف قيم افتراضية — إنه الواجهة الموثَّقة للـ chart. كل مفتاح فيه هو ضبطة يمكن للمشغّلين تجاوزها. صمّمه كما تُصمّم توقيع دالة برمجية: أسماء واضحة، وقيم افتراضية منطقية، ومجمّعة حسب الغرض.

# values.yaml — مثال ذو هيكل جيد replicaCount: 2 image: repository: myregistry.io/myapp tag: "" # فارغ عمداً — يُضبط وقت النشر عبر --set image.tag=1.4.2 pullPolicy: IfNotPresent service: type: ClusterIP port: 8080 ingress: enabled: false # علامة ميزة — يختار المشغّلون تفعيلها className: nginx host: "" tls: [] resources: requests: cpu: "100m" memory: "128Mi" limits: cpu: "500m" memory: "512Mi" autoscaling: enabled: false minReplicas: 2 maxReplicas: 10 targetCPUUtilizationPercentage: 70 podAnnotations: {} # خريطة — تتيح حقن أزواج مفتاح/قيمة اعتباطية

الإشارة إلى القيم في القالب بسيطة: {{ .Values.replicaCount }}. وللمفاتيح المتداخلة: {{ .Values.image.repository }}. يُصيّر Helm القالب كاملاً قبل تطبيقه، لذا يؤدي خطأ مطبعي في مرجع قيمة إلى خطأ وقت التصيير — لا خطأ صامتاً عند التشغيل.

الدوال والخطوط الأنبوبية

يشحن Helm أكثر من 70 دالة من Sprig بالإضافة إلى إضافاته الخاصة. الفكرة الجوهرية هي أنها تتركّب عبر الخطوط الأنبوبية — نموذج Unix pipe مُطبَّق على بيانات القوالب. يصبح خرج دالة ما آخر وسيطة للدالة التالية.

# في ملف قالب — أنماط الدوال الشائعة # quote: يُحيط القيمة بعلامات اقتباس مزدوجة (مطلوب لقيم env var النصية) env: - name: APP_ENV value: {{ .Values.appEnv | quote }} # default: قيمة احتياطية عندما تكون القيمة فارغة image: {{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }} # upper / lower / title — تحويلات نصية - name: LOG_LEVEL value: {{ .Values.logLevel | upper | quote }} # toYaml + nindent — أهم خط أنبوبي في Helm # تفريغ قيمة معقدة (خريطة أو قائمة) كـ YAML مُعاق في المكان resources: {{ toYaml .Values.resources | nindent 10 }} # tpl — تقييم قيمة نصية كقالب (مفيد لبناء روابط URL) annotations: "external-dns.alpha.kubernetes.io/hostname": {{ tpl .Values.ingress.host . | quote }} # include + nindent (للقوالب المُسمّاة — يُغطّى في الدرس الخامس) metadata: labels: {{ include "myapp.labels" . | nindent 4 }} # sha256sum — حقن تعليق توقيعي لإعادة تشغيل الـ pods عند تغيير configmap annotations: checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
نمط إنتاجي — إعادة تشغيل الـ Pod الإجباري عند تغيير ConfigMap: لا تُعيد Kubernetes تشغيل الـ Pods عند تغيير ConfigMap. تُشفّر حيلة sha256sum محتوى ConfigMap المُصيَّر في تعليق مواصفات الـ Pod داخل الـ Deployment. أي تغيير في ConfigMap يُغيّر التجزئة، مما يُغيّر مواصفة الـ Pod، وتعامل Kubernetes ذلك كتحديث متدرّج. هذه ممارسة شبه عالمية في Charts الناضجة.
فخ شائع — الخلط بين nindent وindent: تُضيف indent N مسافة N لكل سطر من مدخلها دون إضافة سطر جديد في البداية. أما nindent N فتُضيف سطراً جديداً أولاً ثم تُعيق. في YAML، المسافات الزائدة أو الناقصة تُفسد البنية بصمت. استخدم دائماً nindent بعد مفتاح يتوقع كتلة، واستخدم toYaml | nindent X بدلاً من تنسيق القيم المتداخلة يدوياً.

التعابير الشرطية

تستخدم الشروط في Helm صيغة if / else if / else / end الخاصة بقوالب Go. الشرط كاذب في هذه الحالات: false و0 والسلسلة الفارغة وnil والقائمة الفارغة والخريطة الفارغة. كل شيء آخر صحيح.

# templates/ingress.yaml — تصيير شرطي للمورد بأكمله {{- if .Values.ingress.enabled }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: {{ include "myapp.fullname" . }} {{- with .Values.ingress.annotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} spec: {{- if .Values.ingress.className }} ingressClassName: {{ .Values.ingress.className }} {{- end }} rules: - host: {{ .Values.ingress.host | quote }} http: paths: - path: / pathType: Prefix backend: service: name: {{ include "myapp.fullname" . }} port: number: {{ .Values.service.port }} {{- if .Values.ingress.tls }} tls: {{- toYaml .Values.ingress.tls | nindent 4 }} {{- end }} {{- end }}

الشرطة - داخل المحدّدات ({{- و-}}) تحذف المسافات البيضاء والأسطر الجديدة من الجانب المعني. هذا أمر بالغ الأهمية: بدونها، تترك الكتل الشرطية أسطراً فارغة في YAML المُصيَّر قد تُربك المُحلّلات الصارمة أو تُنتج فروقات محيّرة في أدوات GitOps.

كتلة with هي صيغة مركّزة من if — إذ تحمي من القيم الفارغة وتُعيد تعيين . للقيمة في آن واحد، مما يُزيل التكرار:

# templates/deployment.yaml — كتلة with للتعليقات الاختيارية metadata: name: {{ include "myapp.fullname" . }} {{- with .Values.podAnnotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} # مكافئ لكن أكثر تطويلاً: # {{- if .Values.podAnnotations }} # annotations: # {{- toYaml .Values.podAnnotations | nindent 4 }} # {{- end }} # شرط قائم على Capabilities — إنتاج HPA فقط إن كانت autoscaling API متاحة {{- if and .Values.autoscaling.enabled (.Capabilities.APIVersions.Has "autoscaling/v2") }} apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler ... {{- end }}

الحلقات مع range

يُكرّر الإجراء range على القوائم والخرائط. إنه الآلية التي تقف وراء تصيير متغيرات البيئة وتوصيلات الأحجام والحاويات الأولية وأي حقل من نوع قائمة في Kubernetes manifests.

# templates/deployment.yaml — التكرار على قائمة وخريطة # التكرار على قائمة متغيرات بيئة إضافية (كل عنصر خريطة بها name/value) # values.yaml: extraEnv: [{name: "FEATURE_X", value: "on"}, {name: "REGION", value: "us-east-1"}] env: - name: PORT value: {{ .Values.service.port | quote }} {{- range .Values.extraEnv }} - name: {{ .name | quote }} value: {{ .value | quote }} {{- end }} # التكرار على خريطة — range يمنحك ($key, $val) أو $val فقط # values.yaml: labels: {team: platform, env: prod} {{- range $key, $val := .Values.extraLabels }} {{ $key }}: {{ $val | quote }} {{- end }} # حلقة مع فهرس — مفيد حين يهم الترتيب (مثل init containers) {{- range $i, $container := .Values.initContainers }} - name: init-{{ $i }} image: {{ $container.image | quote }} command: {{ toYaml $container.command | nindent 6 }} {{- end }}
Helm range loop rendering list to YAML extraEnv (list in values.yaml) name: FEATURE_X value: "on" name: REGION value: "us-east-1" name: TIMEOUT value: "30s" range {{- range . }} dot = كل عنصر يُصيَّر 3 مرات YAML المُصيَّر (كتلة env) - name: "FEATURE_X" value: "on" - name: "REGION" value: "us-east-1" - name: "TIMEOUT" value: "30s"
يُكرّر إجراء range على قائمة في values.yaml ويُصيّر كتلة القالب مرة لكل عنصر، بانياً قائمة YAML النهائية.

تجميع كل شيء — قالب Deployment إنتاجي

القالب التالي تمثيلي لما تجده في chart ناضج وجاهز للإنتاج. يجمع جميع المفاهيم من هذا الدرس: مراجع القيم، وخطوط أنبوبية مع toYaml | nindent، وكتل ذات أعلام ميزات، وفحوصات القدرات، وحلقة range لمتغيرات البيئة.

{{- /* templates/deployment.yaml */ -}} apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "myapp.fullname" . }} namespace: {{ .Release.Namespace }} labels: {{- include "myapp.labels" . | nindent 4 }} {{- with .Values.deploymentAnnotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} {{- end }} selector: matchLabels: {{- include "myapp.selectorLabels" . | nindent 6 }} template: metadata: annotations: checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} labels: {{- include "myapp.selectorLabels" . | nindent 8 }} spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - containerPort: {{ .Values.service.port }} protocol: TCP env: - name: PORT value: {{ .Values.service.port | quote }} {{- range .Values.extraEnv }} - name: {{ .name | quote }} value: {{ .value | quote }} {{- end }} resources: {{- toYaml .Values.resources | nindent 12 }} {{- if .Values.livenessProbe }} livenessProbe: {{- toYaml .Values.livenessProbe | nindent 12 }} {{- end }}
تنقيح القوالب: استخدم helm template myapp ./mychart --values prod.yaml لتصيير القوالب محلياً دون لمس الكلاستر. أنبّب الناتج عبر | grep -A5 "kind: Deployment" للتركيز على مورد واحد. للفحص العميق، يضرب helm install myapp ./mychart --dry-run --debug API الكلاستر للتحقق لكن لا يُطبّق شيئاً — كما يطبع القيم المحسوبة للتحقق من صحة سلسلة التجاوز.

إتقان محرّك قوالب Helm — تدفق القيم، وخطوط أنبوبية الدوال، والشرطات الحاذفة للمسافات البيضاء، وكتل with وrange وif، والنقطة الجذر مقابل النطاقية — هو ما يُميّز chart يعمل في بيئة واحدة عن chart يُشغّل منصة متعددة المستأجرين ومتعددة البيئات بثقة. يمتدّ الدرس الخامس بهذا الموضوع نحو القوالب المُسمّاة والمساعدات لإزالة التكرار عبر الملفات.