Kubernetes مقابل Docker: فهم الفروقات الجوهرية مع أمثلة عملية
في عالم تطوير البرمجيات الحديث، أصبح Docker و Kubernetes أداتين محوريتين لا غنى عنهما في ترسانة أي مطور تقني. كلاهما يوفر القدرة على تغليف التطبيقات داخل حاويات (containers) وتشغيلها بمرونة عبر بيئات مختلفة. ورغم أن كلا التقنيتين تتيحان تحقيق أهداف متشابهة، إلا أن استخداماتهما العملية تختلف بشكل جوهري. في هذا المقال الشامل، سنقدم شرحًا مفصلاً لكل من Docker و Kubernetes، وسنبني تطبيق ويب بسيط باستخدام Node.js، ثم نقوم بنشره مستخدمين كلتا التقنيتين لتوضيح الفروقات العملية بينهما.
ما هو Docker؟ تقنية الحاويات الرائدة
يُعرّف Docker على نطاق واسع بأنه منصة قوية لتغليف التطبيقات واعتمادياتها في “حاويات افتراضية” يمكن تشغيلها على أي خادم Linux. هذه القدرة تتيح للتطبيقات العمل بسلاسة في بيئات متنوعة، سواء كانت خوادم محلية، سحابة عامة، أو سحابة خاصة. يعتمد Docker على ميزات عزل الموارد في نواة Linux، مثل cgroups و kernel namespaces، بالإضافة إلى نظام ملفات قادر على الاتحاد مثل OverlayFS. يتيح ذلك تشغيل الحاويات ضمن مثيل Linux واحد، متجنبًا بذلك الحمل الزائد الناتج عن تشغيل وصيانة الأجهزة الافتراضية التقليدية (VMs).
باختصار، Docker هو بيئة تشغيل للحاويات غير القابلة للتغيير (immutable containers) التي تعمل بأداء قريب من الأداء الأصلي (native performance) على الجهاز المستهدف. وعلى الرغم من وجود بدائل لـ Docker تتمتع بخصائص مماثلة مثل LXC و rkt و containerd، إلا أن Docker يظل الأكثر شعبية واستخدامًا على نطاق واسع في الصناعة.
ما هو Kubernetes؟ نظام أوركسترا الحاويات
يُعرف Kubernetes بأنه نظام مفتوح المصدر لأتمتة نشر، توسيع، وإدارة تطبيقات الحاويات. يُقدم Kubernetes مجموعة من “اللبنات الأساسية” (primitives) التي توفر آليات لنشر التطبيقات وصيانتها وتوسيع نطاقها بناءً على مقاييس مثل استخدام وحدة المعالجة المركزية (CPU)، الذاكرة (memory)، أو مقاييس مخصصة أخرى. يتميز Kubernetes بكونه نظامًا مرنًا وقابلاً للتوسيع بشكل كبير لتلبية أعباء العمل المختلفة.
تُقدم هذه المرونة بشكل كبير من خلال واجهة برمجة تطبيقات Kubernetes API، والتي تُستخدم من قِبل المكونات الداخلية للنظام، بالإضافة إلى الإضافات والحاويات التي تعمل على Kubernetes. يمارس النظام سيطرته على موارد الحوسبة والتخزين عن طريق تعريف الموارد كـ Objects (كائنات)، والتي يمكن إدارتها بعد ذلك بهذه الصفة.
باختصار، Kubernetes يدير مجموعة من المضيفين (hosts) وينشر الحاويات عليها. التقنية الأكثر استخدامًا لتشغيل الحاويات على هؤلاء المضيفين هي Docker. الآن بعد أن فهمنا الأساسيات، لننتقل إلى الجانب العملي ونختبر الفروقات بأنفسنا.
بناء ونشر تطبيق ويب Node.js باستخدام Docker و Kubernetes: دليل عملي
لنتعمق في الجانب العملي ونرى كيف يمكننا بناء ونشر تطبيق ويب بسيط باستخدام كلتا التقنيتين.
1. إعداد بيئة العمل: تثبيت Docker
إذا لم تكن قد قمت بتثبيت Docker بعد، فيجب عليك القيام بذلك. يمكنك مراجعة وتثبيت Docker من خلال زيارة الموقع الرسمي: docs.docker.com/get-docker/.
بعد التثبيت، تأكد من عمل Docker بشكل صحيح عبر التحقق من إصداره:
$ docker --version
Docker version 19.03.13, build 4484c46d9d
2. إنشاء تطبيق ويب Node.js بسيط
سنقوم بإنشاء ملف حزمة Node.js وإضافة اعتمادية خادم ويب واحدة تسمى Express.
ملف package.json: تعريف المشروع
// file: package.json
{
"name": "docker-vs-k8s",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.1"
}
}
ملف server.js: خادم الويب الأساسي
بالإضافة إلى ذلك، نحتاج إلى بدء خادم الويب وتحديد نقطة نهاية واحدة:
// file: server.js
const express = require('express');
// Constants
const PORT = 8080;
const HOST = '0.0.0.0';
// App
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
ملاحظة: لا بأس في تخطي الخطوة التالية إذا لم يكن لديك Node.js مثبتًا. ستستخدم الخطوة التي تليها صورة Docker تأتي مع بيئة Node.js جاهزة للاستخدام. إذا كان لديك Node.js مثبتًا على جهازك المحلي، فيمكنك محاولة تشغيل التطبيق مباشرة:
$ npm install
$ node server.js
Running on http://0.0.0.0:8080
افتح http://localhost:8080 في متصفحك، وسترى استجابة “Hello World“.
3. بناء صورة Docker لتطبيق Node.js
لنجد صورة Docker أساسية لتشغيل تطبيقنا. يُعد Docker Hub العام مصدرًا ممتازًا. إذا بحثت عن ‘node‘ ستجد بسرعة صورة تم استخدامها أكثر من مليار مرة.
تحتاج الحاوية إلى التجميع من أساسها. نبدأ من صورة أساسية تحتوي على بيئة Node.js جاهزة للاستخدام. عادةً ما تُبنى هذه الصورة على صورة Linux بسيطة. ننسخ جميع الملفات المطلوبة إلى الحاوية. بعد ذلك، ننفذ الأوامر، على سبيل المثال، جلب جميع الاعتماديات المطلوبة. الخطوة الأخيرة هي إخبار الحاوية بالأمر الذي يجب تشغيله عند بدء تشغيلها.
ملف Dockerfile: وصف بناء الصورة
# file: 'Dockerfile'
# lts-alpine means long term support and alpine is a very small Linux
# distribution that is a lot smaller than the default one (node:lts).
# smaller images mean faster builds and startup time that is very handy
# when it comes to scaling containers for production up and down
FROM node:lts-alpine
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
COPY package*.json ./
# Install all dependencies
RUN npm install
# Copy sources
COPY server.js server.js
# Command to run when the container starts
CMD [ "node", "server.js" ]
الآن، لنقم ببناء الصورة:
docker build -t node-web-app .
يمكننا تشغيل حاوية Docker عن طريق:
$ docker run --name my_container -p 8080:8080 node-web-app
Running on http://0.0.0.0:8080
افتح http://localhost:8080 في متصفحك وسترى صفحة “Hello World“. هذه المرة، يعمل التطبيق معزولًا داخل حاوية. لا تحتاج حتى إلى Node.js أو أي شيء آخر لبناء وتشغيل هذه الحاوية. كل شيء مغلف، وبفضل طبيعة Docker، يعمل بأداء قريب من الأداء الأصلي.
لنوقف هذه الحاوية التي قد لا تزال تعمل في الخلفية:
$ docker rm -f my_container
ملاحظة هامة: الأداء القريب من الأداء الأصلي (native performance) ينطبق فقط على مضيفي Linux. بالنسبة لأنظمة Mac OS و Windows، يتطلب الأمر بعض الترجمة والمحاكاة الافتراضية التي تأتي مع بعض التدهور في الأداء. لأغراض التطوير، يجب أن يكون الأمر مقبولاً. الأهم من ذلك، أن خوادم الإنتاج عادةً ما تعمل بنظام Linux أصلي يتوافق بشكل ممتاز مع Docker.
4. نشر تطبيق الويب على Kubernetes
الخطوة التالية هي استخدام الحاوية التي بنيناها سابقًا في مجموعة Kubernetes. في هذا الدليل، سنركز على مجموعة محلية. إذا كنت ستنتقل إلى مجموعة بعيدة، فالخطوات متشابهة جدًا. في الإعداد البعيد، ستحتاج أيضًا إلى دفع صورتك إلى سجل متاح للعامة، مما يسمح لمجموعتك البعيدة بالوصول إلى الصورة.
تفعيل Kubernetes في Docker Desktop
يأتي Docker Desktop الخاص بك بالفعل مع تكامل Kubernetes. افتح تطبيق Docker، انتقل إلى “Settings” -> “Kubernetes“، وقم بتمكين Kubernetes. قد يستغرق تطبيق التغيير بعض الوقت. ستكون جاهزًا بمجرد أن يصبح حالة Kubernetes في الشريط السفلي لتطبيق Docker باللون الأخضر. إذا واجهت أي مشاكل، انتقل إلى “Troubleshooting” (أيقونة الخطأ الصغيرة في الزاوية العلوية اليمنى)، واضغط على “Reset to factory defaults“. بعد ذلك، يجب أن يعاد تشغيل Docker وستحتاج إلى تفعيل Kubernetes مرة أخرى.
تثبيت kubectl: أداة سطر الأوامر لـ Kubernetes
لنقم بتثبيت kubectl، وهي إحدى أهم الأدوات للتفاعل مع مجموعة Kubernetes الخاصة بك. اتبع هذا الدليل لتثبيته: kubernetes.io/docs/tasks/tools/install-kubectl/.
الآن، يمكننا التحقق مما إذا كان كل شيء قد تم إعداده بشكل صحيح:
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3m14s
نشر الحاوية في Kubernetes باستخدام Deployment
لنقم بنشر حاوية Docker الخاصة بنا في مجموعتنا:
# file 'application/deployment.yaml'
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-web-app
spec:
selector:
matchLabels:
app: node-web-app
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: node-web-app
spec:
containers:
- name: node-web-app
image: node-web-app
# only use this to for local development
# we never pushed our image to a remote registry
# and by default Kubernetes pulls images
# this property forces kubernetes to always use
# the local image that is not a good practice in production
imagePullPolicy: Never
ports:
- containerPort: 8080
ملف deployment.yaml هو ملف يصف عملية النشر المراد القيام بها. يمكننا تنفيذه عن طريق:
$ kubectl apply -f application/deployment.yaml
deployment.apps/node-web-app created
ولنتحقق مما إذا كانت الحاويات تعمل:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
node-web-app-6788cfd6cc-bcbb2 1/1 Running 0 3s
node-web-app-6788cfd6cc-t5t6w 1/1 Running 0 3s
يدير Kubernetes الخاص بنا مجموعة تحتوي على مضيف واحد، وهو جهازنا المحلي. في مجموعة بعيدة، قد يكون هناك المئات من العقد (nodes) التي تستضيف عمليات نشر مختلفة. لقد قام بنشر حاويتين في بيئتنا. تعمل هذه الحاويات في شبكة معزولة. وإلا، فلن يكون من الممكن كشف نفس المنفذ مرتين.
الوصول إلى التطبيق باستخدام Service
إذًا كيف نصل إلى الحاوية الفعلية؟ يمكنك الوصول إلى حاوية منشورة عن طريق تعريف ما يسمى بـ “Service“. كل تطبيق عام يحتاج إلى Service أمامه يحدد المنفذ العام المكشوف.
# file 'application/service.yaml'
apiVersion: v1
kind: Service
metadata:
name: my-service-for-my-webapp
spec:
type: LoadBalancer
selector:
app: my-example-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
نقوم بتعيين منفذ الحاوية 8080 إلى منفذ متاح للعامة 80. يعمل الـ Service كـ “موازن تحميل” (LoadBalancer)، حيث يوزع الطلبات بين الحاويات.
لنقم بنشر الـ Service الخاص بنا:
$ kubectl apply -f ./application/service.yaml
service/my-service-for-my-webapp created
يمكننا التحقق مما إذا كان الـ Service الخاص بنا يعمل:
$ kubectl describe svc my-service-for-my-webapp
Name: my-service-for-my-webapp
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=my-example-app
Type: LoadBalancer
IP: 10.104.18.24
LoadBalancer Ingress: localhost
Port: <unset> 80/TCP
TargetPort: 8080/TCP
NodePort: <unset> 32114/TCP
Endpoints: 10.1.0.17:8080,10.1.0.18:8080
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
الناتج وصفي للغاية ويؤكد ما نريد تحقيقه. يستخدم نقاط النهاية من حاويتين منشورتين (تسمى “pods” في Kubernetes).
الآن، يمكنك فتح http://localhost:80.
هذا كل شيء! لقد قمت بإنشاء حاوية Docker واستخدمتها في مجموعة Kubernetes الخاصة بك. هذا الإعداد قوي ويُعد الأساس للعديد من المنتجات والشركات القابلة للتوسع في الوقت الحاضر.
الخاتمة: تنظيف بيئة العمل
لإنهاء تجربتنا، دعونا ننظف مساحة العمل الخاصة بنا:
$ kubectl delete -f ./application/service.yaml
service "my-service-for-my-webapp" deleted
$ kubectl delete -f application/deployment.yaml
deployment.apps "node-web-app" deleted
للحفاظ على موارد جهازنا حرة، يجب علينا أيضًا إيقاف ميزة Kubernetes في Docker Desktop.
آمل أن تكون قد استمتعت بهذا المثال العملي. حفز نفسك للبحث أكثر، والتحقق من أمثلة أخرى، ونشر الحاويات، وربطها، واستخدامها. ستتعلم العديد من الميزات الرائعة في المستقبل التي ستمكنك من شحن تطبيقك إلى الإنتاج بطريقة سهلة، قابلة لإعادة الاستخدام، وقابلة للتوسع.
الخلاصة التقنية
في هذا المقال، استعرضنا الفروقات الجوهرية بين Docker و Kubernetes، حيث يمثل Docker الأداة الأساسية لتغليف التطبيقات في حاويات معزولة وفعالة، بينما يتولى Kubernetes مهمة أوركسترا هذه الحاويات وإدارتها على نطاق واسع. Docker يوفر بيئة تشغيل موحدة للتطبيقات، مما يضمن اتساقها عبر البيئات المختلفة، بينما Kubernetes يضيف طبقة من التعقيد والقوة لإدارة هذه الحاويات، موفرًا ميزات مثل التوسع التلقائي (auto-scaling)، التوزيع (load balancing)، والشفاء الذاتي (self-healing).
لقد أظهرنا عمليًا كيف يمكن لتطبيق Node.js بسيط أن يُنشر أولاً كحاوية Docker مستقلة، ثم يُدار ويُوسع نطاقه داخل مجموعة Kubernetes. هذا التكامل بين الأداتين هو حجر الزاوية في بنى التطبيقات الحديثة القائمة على الخدمات المصغرة (microservices) والسحابة. فهم هذه الفروقات وكيفية استغلال كل منهما يمثل مهارة حاسمة لأي مطور أو مهندس DevOps يسعى لبناء أنظمة قوية، مرنة، وقابلة للتوسع.