دليل شامل: استكشاف مدير الحزم Helm لمنصة Kubernetes
مقدمة إلى عالم Kubernetes و Helm
في عالم تطوير ونشر التطبيقات الحديث، أصبحت إدارة التعقيدات المتزايدة للبنية التحتية أمرًا حتميًا. تُعد Kubernetes، المعروفة اختصارًا بـ K8s، نظامًا مفتوح المصدر رائدًا لأتمتة نشر التطبيقات المعبأة في حاويات، وتوسيع نطاقها، وإدارتها. ولكن، حتى مع قوة Kubernetes، يظل هناك تحدٍ في تبسيط عمليات النشر المعقدة. هنا يأتي دور Helm، مدير الحزم لـ Kubernetes، الذي يحول هذه العمليات إلى تجربة سلسة وفعالة. قبل الغوص في تفاصيل Helm، دعونا نستعرض بعض المفاهيم الأساسية لنشر التطبيقات وأساسيات مصطلحات Kubernetes.
ما هي Kubernetes؟
Kubernetes (K8s) هو نظام مفتوح المصدر مصمم لأتمتة نشر، توسيع، وإدارة التطبيقات المعبأة في حاويات. يمكن وصف Kubernetes بأنه مجموعة متطورة من واجهات برمجة التطبيقات (APIs) التي تسهل نشر وإدارة وتوسيع نطاق التطبيقات. يتم تغليف التطبيقات باستخدام Docker، ثم يتم التعبير عن المنطق المحيط بنشر التطبيق باستخدام قوالب Helm. هذه القوالب هي في الأساس تعليمات يتم تشغيلها لاحقًا عبر Kubernetes API. هناك عدد كبير من حزم Helm الجاهزة التي تلبي احتياجات نشر التطبيقات المختلفة. يمكن النظر إلى Kubernetes و Helm معًا كحل متكامل لاحتياجات DevOps للتطبيقات.
ملاحظة ممتعة: الثيمة البحرية في عالم الحاويات
يتميز النظام البيئي للحاويات بالكامل، بما في ذلك Docker، بموضوع بحري شيق. فـ Docker يرمز للحيتان، و Kubernetes لديه ‘قرون’ (pods) من الحيتان، وشعاره يشبه جزء التوجيه في السفينة، أما Helm فهو دفة السفينة. أليس هذا لطيفًا؟

نشر التطبيقات على Kubernetes: فهم الطبقات الأساسية
بغض النظر عن مكان نشر التطبيق، هناك مفاهيم أساسية تظل ثابتة. سواء كنت تنشر على جهاز الكمبيوتر المحمول الخاص بك، أو خادم بعيد، أو مثيل AWS EC2، أو أنظمة الحوسبة عالية الأداء، أو Kubernetes، فإن المفاهيم الأساسية لا تتغير. يمكننا التفكير في معظم المفاهيم التقنية كسلسلة من الطبقات. بمجرد فهم هذه الطبقات الأساسية، يمكنك البدء في بناء حلولك.
طبقات التطبيق الأساسية في Kubernetes
- طبقة البيانات:
PVCأوPersistent Volume Claims - طبقة التطبيق:
Pods - طبقة الخدمات:
Services(SVC)
دعونا نتناول كل طبقة على حدة.
طبقة البيانات / مطالبات الحجم الدائم (Persistent Volume Claims – PVCs)
هذه الطبقة واضحة ومباشرة. عندما تحتاج إلى الاحتفاظ بالبيانات، فإنك تحتفظ بها في نظام ملفات (filesystem). يمكن أن يكون هذا تخزينًا محليًا أو نوعًا من أنظمة الملفات الشبكية (NFS). إذا كنت تستخدم قاعدة بيانات، فإن قاعدة البيانات تحتفظ ببياناتها في النهاية في نظام ملفات.
طبقة التطبيق / القرون (Pods)
طبقة التطبيق هي ما نفكر فيه عادة عند النشر. إنها الجزء الذي نقوم فيه بتشغيل أوامر مثل أو apt-get install أو npm run. يمكن أن يكون التطبيق خادم ويب docker runNGINX، أو تطبيق Python أو Node.js، أو تطبيق Spark على سبيل المثال. يتم تصنيف التطبيقات إما كـ Kubernetes Deployments أو Stateful Set، اعتمادًا على ما إذا كانت تحتفظ بالبيانات (أي لديها حالة) أم لا. قاعدة بيانات MySQL ستكون مثالاً لتطبيق Stateful، حيث تحتاج إلى تتبع المعلومات الخاصة بها. بينما خادم NGINX سيكون Kubernetes Deployment، لأنه لا يحتاج إلى تتبع أي معلومات عن نفسه — فهو stateless (عديم الحالة).
طبقة الخدمات / SVC
طبقة الخدمات هي المكان الذي نعرض فيه تطبيقنا للعالم الخارجي. يتم تحقيق ذلك عادةً بالقول: ‘لدي تطبيق يعمل على هذا المنفذ’. قد تكون قد قمت بتشغيل هذه الخدمات مباشرة، أو قمت بعمل شيء مثل تمرير الوكيل (proxy pass) في NGINX أو Apache.
طبقات موثوقية الموقع (Site Reliability)
موثوقية الموقع هي قدرتنا على القول بثقة أن تطبيقنا يعمل، وسيبقى على الأرجح يعمل! في الواقع، نحن نريد واجهة برمجة تطبيقات (API) تقوم بذلك بشكل أساسي.

مكونات موثوقية Kubernetes العامة
- المراقبة:
metrics-server - التوسع (أو موازنة التحميل):
Horizontal Pod Autoscaler (HPA) - قواعد الخدمة: مواصفات الحاويات
طبقة المراقبة / Metrics Server
تجيب طبقة المراقبة على السؤال: ‘كيف يعمل تطبيقنا؟’. من الناحية المثالية، ستجيب على أسئلة مثل: ‘كم تبقى من وحدة المعالجة المركزية (CPU) على هذا الجهاز؟’ و ‘هل نفدنا من الذاكرة بعد؟’.
طبقة التوسع / HPA
هل سبق لك أن واجهت تطبيقًا يعمل بشكل رائع حتى بدأ عدد كبير جدًا من الأشخاص في استخدامه في وقت واحد؟ يمكنك معالجة ذلك عن طريق توسيع نطاق مثيلات تطبيقك (scaling up) أو تقليصها (scaling down). مع تطبيقات الويب، غالبًا ما ترى مصطلح موازنة التحميل (load balancing) أيضًا. هذه الوظيفة مدمجة في العديد من مديري العمليات وخوادم https مثل PM2 و Gunicorn. في Kubernetes، يمكنك تحقيق ذلك باستخدام Horizontal Pod Autoscaler، أو HPA، والذي تمنحه قواعد محددة لكيفية التوسع أو التقليص.
طبقة قواعد الخدمة
هل أردت يومًا أتمتة متى/كيف يجب أن يعاد تشغيل تطبيقك؟ ربما تريده أن يعاد تشغيله 3 مرات ثم يتوقف. أو ربما تريده أن يعاد تشغيله، ولكن ليس على الفور. امنحه بعض الوقت! قد ترغب أيضًا في وجود مقياس موضوعي لاختبار ما إذا كان تطبيقك يعمل أم لا.
نشر التطبيقات على Kubernetes
يمكن نشر تطبيقات Kubernetes إما من خلال واجهة سطر الأوامر (CLI)، أو عن طريق كتابة قوالب YAML التي تصف طبقات PVCs و Pods (سواء كانت Deployments أو Stateful sets) و Service (SVC) المختلفة.
مدير الحزم Helm: تبسيط نشر Kubernetes
Helm هو أفضل طريقة للعثور على البرامج المصممة لـ Kubernetes ومشاركتها واستخدامها. https://helm.sh/
يتيح لنا مدير الحزم Helm ربط عمليات نشر Kubernetes المعقدة في حزمة واحدة، يمكن تثبيتها بأمر واحد. يستخدم Helm لغة قوالب (templating language) فوق تعريفات Kubernetes YAML للسماح بمرونة أكبر لعمليات النشر لدينا. ربما تكون النقطة الأكثر أهمية التي يجب ملاحظتها مع Helm هي أنه قد تم قبوله على نطاق واسع من قبل المجتمع. هذا يعني أن هناك الكثير من الموارد لاستخدام Helm، والبدء به، وأيضًا الكثير من مخططات Helm Charts المكونة مسبقًا! من النادر جدًا أن أضطر إلى إنشاء حزمة Helm بالكامل من الصفر. يمكنني دائمًا تقريبًا العثور على نقطة بداية جيدة من واحد أو أكثر من مخططات Helm المتاحة بالفعل.
نشر NGINX على Kubernetes بدون Helm
لنبدأ بالحديث عن نشر NGINX الأساسي بدون Helm. كما ترون، هناك الكثير من الأشياء التي يجب تتبعها وربما لن نقوم بكتابة هذا يدويًا. هنا يأتي دور مدير الحزم Helm، ولكن من الجيد إلقاء نظرة لمعرفة ما يحدث أولاً!
# Source: nginx/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app.kubernetes.io/name: nginx
helm.sh/chart: nginx-6.0.1
app.kubernetes.io/instance: nginx
app.kubernetes.io/managed-by: Helm
spec:
selector:
matchLabels:
app.kubernetes.io/name: nginx
app.kubernetes.io/instance: nginx
replicas: 1
template:
metadata:
labels:
app.kubernetes.io/name: nginx
helm.sh/chart: nginx-6.0.1
app.kubernetes.io/instance: nginx
app.kubernetes.io/managed-by: Helm
spec:
containers:
- name: nginx
image: docker.io/bitnami/nginx:1.19.0-debian-10-r2
imagePullPolicy: "IfNotPresent"
ports:
- name: http
containerPort: 8080
livenessProbe:
failureThreshold: 6
initialDelaySeconds: 30
tcpSocket:
port: http
timeoutSeconds: 5
readinessProbe:
initialDelaySeconds: 5
periodSeconds: 5
tcpSocket:
port: http
timeoutSeconds: 3
فهم تعريف نشر Kubernetes
دعونا نفكك الأجزاء المختلفة لتعريف نشر Kubernetes.
البيانات الوصفية (Metadata)
أريد أن أتطرق بإيجاز شديد إلى . بإيجاز فقط، لأنه من المحتمل أن تكون بخير مع الإعدادات الافتراضية ولن تحتاج إلى لمسها. أحد أهداف labelsKubernetes هو أنه يجب أن يقوم بتجريد الخادم المادي الفعلي. لا يجب أن تهتم عادةً إذا كان تطبيقك يعمل على أو node1. بالطبع في مرحلة ما تهتم، وعندها ستبدأ في استخدام node2labels. حتى ذلك الحين، لا تقلق بشأنها والتزم بالإعدادات الافتراضية.
الحاويات (Containers)
هذا هو الجزء الأكثر أهمية بالنسبة لك عند نشر التطبيقات. تحتاج إلى تعريف حاوياتك. يمكن أن يحتوي Pod نشر واحد على العديد من الحاويات. تحتوي هذه الحاوية، على الأقل، على و name و repo:tag
containers:
- name: nginx
#image: "repo:tag"
image: docker.io/bitnami/nginx:1.19.0-debian-10-r2
بمجرد أن يكون لديك الأساس، تحتاج إلى تعريف المنافذ التي سيتم التقاطها بواسطة الخدمة. هل ترى هذا الفصل في الاهتمامات؟
ports:
- name: http
containerPort: 8080
قواعد التطبيق (App Rules)
ثم، في مرحلة ما، نريد أن نعرف ما إذا كان تطبيقنا يعمل. يمكننا حتى تحديد مكانه بالضبط في دورة حياته باستخدام الخطافات (hooks) المختلفة:
livenessProbe:
failureThreshold: 6
initialDelaySeconds: 30
tcpSocket:
# This corresponds to the ports[0].name
port: http
timeoutSeconds: 5
readinessProbe:
initialDelaySeconds: 5
periodSeconds: 5
# This corresponds to the ports[0].name
tcpSocket:
port: http
timeoutSeconds: 3
الأسماء (Names)
هذا مفهوم عام أكثر، لكنني أريد أن أشير إلى أن تسمية الأشياء مهمة جدًا في النظام البيئي لـ Kubernetes. لاحظ أننا أعطينا و container اسمًا (port). لاحقًا عندما نحتاج إلى الإشارة إليهما، نستخدم هذا الاسم (name).name
نشر NGINX على Kubernetes باستخدام مخطط Helm (Helm Chart)
ينشئ مدير الحزم Helm سلسلة من القوالب التي يمكن تعديلها من خلال واجهة سطر الأوامر (Helm CLI). يتوافق كل من هذه القوالب مع أحد أنواع Kubernetes التي ناقشناها سابقًا. إليك مثال على مخطط bitnami/nginx Helm chart:

قوالب مخطط Bitnami/NGINX Helm Chart
إليك نفس الكتلة مع لغة قوالب Helm. من أجل الإيجاز، لقد حذفت بعض أجزاء القالب. إذا كنت ترغب في رؤية كل شيء، يمكنك إلقاء نظرة عليه في مستودع GitHub. (هذا لأغراض العرض التوضيحي وليس مخطط Helm chart وظيفيًا بالكامل. يرجى عدم استخدامه. احصل على مخطط Helm chart الفعلي بدلاً من ذلك.)
# Source: nginx/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "nginx.fullname" . }}
labels:
{{- include "nginx.labels" . | nindent 4 }}
spec:
selector:
matchLabels:
{{- include "nginx.matchLabels" . | nindent 6 }}
replicas: {{ .Values.replicaCount }}
template:
metadata:
labels:
{{- include "nginx.labels" . | nindent 8 }}
# Omitted the annotation labels!
spec:
containers:
- name: nginx
image: {{ template "nginx.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
ports:
- name: http
containerPort: {{ .Values.containerPort }}
{{ if .Values.containerTlsPort }}
- name: https
containerPort: {{ .Values.containerTlsPort }}
{{ end }}
{{- if .Values.livenessProbe }}
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
{{- end }}
{{- if .Values.readinessProbe }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
{{- end }}
{{- if .Values.resources }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- end }}
من أين تأتي قيم قوالب Helm؟
هذا هو ما يعجبني حقًا في Helm. القيم التي تظهر في القالب تأتي من أحد مكانين:
الدوال القالبية (Templated Functions)
تأتي القيم من القالب نفسه، كما هو موضح هنا:
{{ template "nginx.fullname" . }}
يمكننا أن نجد أن هذا معرف في ملف الخاص بنا، وهي طريقة للحصول على دوال أكثر تعقيدًا مما يمكننا الحصول عليه بملف templates/_helpers.tpl عادي.yaml
# templates/_helpers.tpl
{{ /* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). */ }}
# Here is the nginx.fullname
{{ - define "nginx.fullname" - }}
{{ - if .Values.fullnameOverride - }}
{{ - .Values.fullnameOverride | trunc 63 | trimSuffix "-" - }}
{{ - else - }}
{{ - $name := default .Chart.Name .Values.nameOverride - }}
{{ - if contains $name .Release.Name - }}
{{ - .Release.Name | trunc 63 | trimSuffix "-" - }}
{{ - else - }}
{{ - printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" - }}
{{ - end - }}
{{ - end - }}
القيم المكشوفة في ملف Values.yaml
هذه في الواقع ميزة رائعة وما يجعل Helm قويًا وقابلًا للتكوين. يأتي كل مخطط Helm chart مع ملف . يمكنك وضع ما تريد في ملف values.yaml، ثم استخدامه في جميع أنحاء مخطط values.yamlHelm chart الخاص بك، ويتم عرضه من خلال CLI!
## Bitnami NGINX image version
## ref: https://hub.docker.com/r/bitnami/nginx/tags/
## image:
registry: docker.io
repository: bitnami/nginx
tag: 1.19.1-debian-10-r0
## Specify a imagePullPolicy
## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent'
## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images
## pullPolicy: IfNotPresent
ثم نرى هذا مشارًا إليه في قوالبنا على النحو التالي:
# templates/deployment.yaml
# ...
containers:
- name: nginx
image: {{ template "nginx.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
يمكن أيضًا تعديل كل شيء في من خلال values.yamlHelm CLI:
helm upgrade --install nginx bitnami/nginx \
--set image.tag="my-new-tag"
بعد ذلك، سيتم عرض كـ containers[0].image.image: docker.io/bitnami/nginx:my-new-tag
الخلاصة التقنية
لقد استكشفنا في هذا المقال الجوانب الأساسية لـ Kubernetes ودور Helm كمدير حزم لا غنى عنه. اتضح أن Helm لا يبسط فقط عملية نشر التطبيقات المعقدة عن طريق تجميعها في حزم قابلة للإدارة، بل يعزز أيضًا مرونة التكوين من خلال لغة القوالب وملف . إن تبني المجتمع الواسع لـ values.yamlHelm يعني وفرة في الموارد والمخططات الجاهزة، مما يقلل بشكل كبير من الحاجة إلى البدء من الصفر. فهم هذه الأدوات والطبقات الأساسية لـ Kubernetes يمكن المطورين ومهندسي DevOps من بناء وإدارة أنظمة قوية وقابلة للتوسع بكفاءة أعلى، مما يجعل عملية النشر أقل تعقيدًا وأكثر إنتاجية.