نشر التطبيقات على OpenShift خطوة بخطوة
في عالم تطوير البرمجيات الحديث، لم يعد الهدف فقط هو كتابة تطبيق يعمل على جهاز المطوّر، بل أصبح المطلوب هو بناء تطبيق يمكن تشغيله بثبات، ونشره بسرعة، وتوسيعه بسهولة، ومراقبته باستمرار، وتأمينه في بيئات إنتاج معقدة. هنا يأتي OpenShift كأحد أشهر المنصات التي تبني على Kubernetes وتضيف له طبقة عملية تجعل إدارة التطبيقات أسهل وأكثر ملاءمة لفرق التطوير وفرق العمليات في الوقت نفسه. وعندما نتحدث عن نشر التطبيقات على OpenShift خطوة بخطوة فنحن لا نتحدث عن مجرد نقل ملفات إلى سيرفر، بل عن رحلة كاملة تبدأ من فهم البنية المناسبة للتطبيق، ثم بناء الصورة الحاوية، ثم إعداد الموارد داخل OpenShift، ثم تشغيل التطبيق وربطه بالخارج، وبعد ذلك مراقبته وتوسيع نطاقه وتحديثه بأمان.
ما يميز OpenShift أنه لا يطلب منك أن تفكر في البنية التحتية من الصفر، بل يمنحك مجموعة أدوات ومفاهيم تساعدك على التركيز على التطبيق نفسه. ومع ذلك، فإن النجاح في النشر عليه يحتاج إلى فهم متسلسل: كيف يعمل الـ Project، ما دور الـ BuildConfig، متى تستخدم Deployment أو DeploymentConfig، كيف تنشئ Service وRoute، كيف تمرر الأسرار والمتغيرات، وكيف تتعامل مع مشكلات الصلاحيات والصور الحاوية والاتصال الداخلي. هذا المقال مكتوب ليأخذك من البداية إلى النهاية بطريقة عملية وواضحة، وبأسلوب بشري قريب من التجربة اليومية للمطورين، بعيدًا عن التعقيد النظري الزائد.
ما هو OpenShift ولماذا يستخدمه المطورون؟
OpenShift هو منصة تطبيقات مبنية على Kubernetes، لكنها لا تكتفي بجدولة الحاويات وتشغيلها فقط، بل تضيف مجموعة من الأدوات والخصائص التي تجعل دورة حياة التطبيق أكثر اكتمالًا. في Kubernetes التقليدي قد تحتاج إلى إعدادات إضافية كثيرة كي تصل إلى تجربة نشر سهلة وآمنة، بينما OpenShift يوفر لك طبقة جاهزة تقريبًا للتعامل مع الصورة الحاوية، وبناء التطبيق، وتوزيع الشبكة، وإدارة الـ Routes، وتطبيق سياسات الأمان، وحتى دمج الأدوات الخاصة بالـ CI/CD.
السبب الذي يجعل كثيرًا من المؤسسات تختار OpenShift هو أنه يوفّر توازنًا جيدًا بين المرونة والانضباط. المطور يريد أن يدفع الكود بسرعة، وفرق التشغيل تريد أن تحافظ على الاستقرار والأمان، وOpenShift يحاول أن يجمع الطرفين تحت منصة واحدة. كما أنه مناسب جدًا للتطبيقات المتوسطة والكبيرة التي تحتاج إلى التوسع والـ Rollout التدريجي والتشغيل في بيئات متعددة مثل التطوير والاختبار والإنتاج.
إذا كنت قد عملت سابقًا مع Docker أو Kubernetes، فستجد أن OpenShift ليس غريبًا جدًا عليك، لكنك ستلاحظ أن بعض التفاصيل أكثر صرامة، خصوصًا في موضوع الصلاحيات وتشغيل الحاويات كمستخدم غير root. وهذه نقطة إيجابية في معظم البيئات المؤسسية لأنها تقلل المخاطر الأمنية بشكل ملحوظ.
الفكرة العامة قبل النشر
قبل أن تبدأ النشر فعليًا، من المهم أن تفهم أن التطبيق في OpenShift يمر عادة بعدة مراحل. أولًا لديك الكود المصدر أو الصورة الحاوية. بعد ذلك تحتاج إلى بناء الصورة أو اعتماد صورة جاهزة. ثم تنشئ كائنات Kubernetes/OpenShift التي تعرّف كيف يعمل التطبيق داخل المنصة. هنا يأتي دور الـ Deployment أو DeploymentConfig، ودور الـ Service لتوجيه الاتصال داخل الكلستر، ودور الـ Route لإتاحة التطبيق للعالم الخارجي. وفي كثير من الحالات ستحتاج أيضًا إلى ConfigMap وSecret لتخزين الإعدادات الحساسة وغير الحساسة، ثم تضبط الـ Probes، والـ Resources، والـ Scaling، وربما الـ Persistent Volume إذا كان التطبيق يحتاج إلى تخزين دائم.
في التطبيقات الحديثة، غالبًا ما يكون النشر الجيد هو نتيجة تصميم جيد من البداية. إذا كان التطبيق يعتمد على ملف إعدادات كبير، أو على ملفات محلية داخل الحاوية، أو على جلسات مخزنة محليًا، فستواجه مشكلات عند التوسع. لذلك، قبل أن تنشر، اسأل نفسك: هل التطبيق Stateless أم Stateful؟ هل يحتاج إلى قاعدة بيانات خارجية؟ هل يستطيع العمل إذا تمت إعادة تشغيل الحاوية في أي لحظة؟ هذه الأسئلة ليست نظرية، بل تؤثر مباشرة على طريقة النشر.
المتطلبات الأساسية قبل البدء
لإنهاء عملية النشر بشكل صحيح، ستحتاج إلى بعض الأمور الأساسية. أولها وصول إلى Cluster خاص بـ OpenShift، سواء كان محليًا مثل OpenShift Local أو بيئة اختبار أو إنتاج. ثانيها أداة الأوامر oc، وهي الأداة الرسمية للتعامل مع OpenShift من الطرفية. ثالثها مشروع تطبيقي جاهز، وقد يكون تطبيقًا بلغة Node.js أو Java أو Python أو PHP أو أي تقنية أخرى يمكنك حزمها داخل صورة حاوية. رابعها فهم أساسي لملفات YAML وطريقة تعريف الموارد في Kubernetes/OpenShift.
من المفيد أيضًا أن تتأكد من أن التطبيق يعمل محليًا قبل أن تفكر في نشره. الكثير من المشكلات التي تظهر في OpenShift تبدأ من كود لم يُختبر محليًا جيدًا، أو من اعتماد على متغيرات بيئة غير موجودة، أو من افتراضات حول المسار أو الصلاحيات أو المنافذ. عندما يكون التطبيق مستقرًا محليًا يصبح الانتقال إلى OpenShift أسهل بكثير.
الخطوة الأولى: تجهيز المشروع للتشغيل داخل الحاوية
لنبدأ من أساس مهم جدًا: التطبيق يجب أن يكون جاهزًا للعمل داخل Container. هذا يعني أن كل الاعتماد على النظام المحلي أو على الملفات الثابتة أو على بيئة التطوير يجب أن يتم عزله أو إعادة تصميمه. في بيئة الحاويات، أنت لا تملك جهازًا ثابتًا يمكن الاعتماد عليه، بل لديك حاوية يمكن أن تُنشأ وتُدمّر في أي وقت، لذلك يجب أن تكون كل التهيئة عبر متغيرات بيئة أو ملفات إعدادات أو Secret/ConfigMap، وليس عبر افتراضات مخفية.
مثال بسيط على تطبيق Node.js يمكن حزمُه داخل حاوية:
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
const MESSAGE = process.env.MESSAGE || 'Hello from OpenShift';
app.get('/', (req, res) => {
res.send(`<h1>${MESSAGE}</h1><p>Application is running on port ${PORT}</p>`);
});
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok' });
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server running on port ${PORT}`);
});
لاحظ هنا أننا لم نربط الخادم بـ localhost فقط، بل استخدمنا 0.0.0.0. هذه نقطة حاسمة داخل الحاويات، لأن ربط التطبيق على 127.0.0.1 فقط قد يجعل الخدمة غير قابلة للوصول من الخارج عبر Service. كما أن استخدام متغيرات بيئة مثل PORT و MESSAGE يساعد على جعل التطبيق أكثر مرونة في OpenShift.
الخطوة الثانية: إنشاء Dockerfile مناسب لـ OpenShift
الآن نحتاج إلى صورة حاوية. وفي OpenShift من الأفضل أن تكتب Dockerfile أو Containerfile بشكل يراعي مبادئ الأمان والتشغيل داخل البيئات المقيدة. من الأخطاء الشائعة جدًا أن يبني المطور صورة تعتمد على المستخدم root، أو ينسى تهيئة الصلاحيات، أو يضع التطبيق في مسار لا يملك المستخدم العادي القدرة على الوصول إليه.
هذا مثال مناسب نسبيًا لتطبيق Node.js:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]
في بعض السيناريوهات قد تحتاج إلى ضبط أذونات الملفات، خصوصًا إذا كان التطبيق يكتب ملفات مؤقتة أو ملفات logs. وقد تحتاج أيضًا إلى جعل التطبيق يكتب في /tmp أو في مسار يستطيع المستخدم غير root الوصول إليه. الفكرة هنا أن OpenShift غالبًا يشغل الحاويات بصلاحيات مقيدة، لذلك أي افتراض يخص root قد يصطدم بسياسات المنصة.
إذا كان تطبيقك بلغة Java مثل Spring Boot، فستكون الصورة مختلفة قليلًا، لكن الفكرة نفسها تبقى ثابتة: ضع التطبيق في مسار واضح، اجعل المنفذ معروفًا، ولا تفترض وجود root. مثال:
FROM eclipse-temurin:21-jre-alpine
WORKDIR /opt/app
COPY target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
الخطوة الثالثة: بناء الصورة محليًا واختبارها
قبل أن ترسل الصورة إلى OpenShift، اختبرها محليًا. هذه خطوة صغيرة لكنها توفر ساعات من البحث لاحقًا. استخدم:
docker build -t myapp:1.0 .
docker run -p 3000:3000 -e MESSAGE="Local test" myapp:1.0
ثم افتح المتصفح على http://localhost:3000 أو استخدم curl:
curl http://localhost:3000
إذا لم تعمل الصورة محليًا، فاحتمال كبير أنها لن تعمل داخل OpenShift أيضًا. صحيح أن بعض المشكلات تظهر فقط في البيئة السحابية، لكن النجاح المحلي هو الحد الأدنى الذي ينبغي الوصول إليه.
الخطوة الرابعة: تسجيل الدخول إلى OpenShift وإنشاء مشروع
بعد أن تتأكد من أن التطبيق يعمل، حان وقت الانتقال إلى OpenShift. عادة ستبدأ بتسجيل الدخول:
oc login https://api.cluster.example.com:6443 --token=YOUR_TOKEN
أو عبر المستخدم وكلمة المرور إذا كانت البيئة تسمح بذلك. بعد الدخول، يمكنك إنشاء مشروع جديد:
oc new-project myapp-dev
أو التحقق من المشروع الحالي:
oc project
المشروع في OpenShift يشبه مساحة عمل معزولة نسبيًا، وهذا يمنحك تنظيمًا أفضل بين التطبيقات والفرق. من الجيد جدًا أن تفصل بين بيئات التطوير والاختبار والإنتاج، وأن تتعامل مع كل بيئة كمشروع مستقل أو على الأقل كمساحة منفصلة تنظيميًا.
الخطوة الخامسة: رفع الصورة إلى Registry
هناك أكثر من طريقة للتعامل مع الصور الحاوية في OpenShift. قد تستخدم Registry داخلية خاصة بالمنصة، أو Registry خارجية مثل Quay أو Docker Hub أو Harbor. في بيئات المؤسسات، غالبًا يكون الـ Registry الداخلي أو الخاص هو الخيار الأفضل لأنك تتحكم في الوصول والنسخ والإصدارات.
إذا كنت تريد دفع الصورة من جهازك إلى Registry خارجي، فقد تفعل مثلًا:
docker tag myapp:1.0 registry.example.com/team/myapp:1.0
docker push registry.example.com/team/myapp:1.0
أما إذا كنت تستخدم OpenShift لبناء الصورة من المصدر، فيمكنك الاستفادة من BuildConfig، لكن قبل ذلك من الأفضل أن نفهم الفرق بين البناء والنشر. البناء هو تحويل الكود أو المصدر إلى صورة جاهزة، أما النشر فهو تشغيل تلك الصورة داخل الـ Cluster. أحيانًا يكون البناء داخل OpenShift نفسه، وأحيانًا يكون خارجها في CI/CD ثم تُدفع الصورة إلى Registry، وبعدها يقوم OpenShift بسحبها وتشغيلها.
الخطوة السادسة: إنشاء Deployment أو DeploymentConfig
هذه المرحلة هي قلب عملية النشر. هنا تقول لـ OpenShift كيف يشغل التطبيق: عدد النسخ، الصورة المطلوبة، المنافذ، المتغيرات، الموارد، وسياسات التحديث. في Kubernetes الحديث، غالبًا ستستخدم Deployment. وفي بعض بيئات OpenShift ما زالت DeploymentConfig موجودة ومستخدمة في سياقات معينة، لكن التوجه الحالي يميل أكثر نحو موارد Kubernetes الأصلية مثل Deployment.
مثال على Deployment لتطبيق Node.js:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:
app: myapp
spec:
replicas: 2
selector:
matchLabels:
app: myapp
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: registry.example.com/team/myapp:1.0
ports:
- containerPort: 3000
env:
- name: MESSAGE
value: "Welcome from OpenShift"
- name: PORT
value: "3000"
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 20
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
هذا المثال يوضح مجموعة عناصر أساسية. عدد النسخ هو 2، وهذا يعني أن التطبيق سيعمل عبر نسختين لتوفير توافر أفضل. الـ readinessProbe تحدد متى تكون الحاوية جاهزة لاستقبال الطلبات. والـ livenessProbe تساعد المنصة على اكتشاف الحاويات العالقة وإعادة تشغيلها. والـ resources تمنع التطبيق من استهلاك الموارد بلا حدود.
لا تستخف بإعداد الـ probes. في الواقع، كثير من المشكلات التي تظهر بعد النشر ليست بسبب الكود نفسه، بل بسبب أن المنصة لا تعرف متى تعتبر الحاوية جاهزة أو حية. النتيجة تكون حاوية تدور في إعادة تشغيل مستمرة، أو خدمة تُوجَّه إليها الطلبات قبل أن تكون مستعدة.
الخطوة السابعة: تطبيق الـ Deployment على OpenShift
بعد حفظ ملف YAML، يمكنك تطبيقه مباشرة:
oc apply -f deployment.yaml
ثم راقب الموارد:
oc get deployments
oc get pods
oc describe pod <pod-name>
إذا كانت هناك مشكلة، فالأمر describe غالبًا يكون مفيدًا جدًا لأنه يعرض الأحداث والأسباب المحتملة، مثل فشل سحب الصورة أو رفض المنفذ أو مشكلة الصلاحيات أو فشل الـ Probe.
الخطوة الثامنة: إنشاء Service للوصول الداخلي
الـ Deployment يشغّل الحاويات، لكن الحاويات نفسها لا تكون عادة نقطة الدخول المباشرة لبقية المنصة. لذلك نحتاج إلى Service. الـ Service تعطي عنوانًا ثابتًا داخل الكلستر وتوجّه الطلبات إلى الـ Pods المناسبة.
مثال:
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: ClusterIP
هنا جعلنا الخدمة تستقبل على المنفذ 80 داخليًا ثم تمرر إلى 3000 داخل الحاوية. هذا النمط شائع جدًا لأنه يفصل بين عنوان الخدمة الخارجي والمنفذ الداخلي للتطبيق. بعد تطبيق الملف:
oc apply -f service.yaml
oc get svc
الـ Service مهمة جدًا لأن أي تطبيق آخر داخل المشروع أو داخل الكلستر يمكنه الوصول إلى التطبيق عبر اسم الخدمة بدل الاعتماد على Pod IP المتغير.
الخطوة التاسعة: إنشاء Route للوصول الخارجي
في OpenShift، واحدة من أكثر المزايا شهرة هي الـ Route. هي الطبقة التي تسمح لك بتعريض التطبيق للعالم الخارجي عبر اسم نطاق أو عنوان URL، من دون الدخول في تفاصيل Ingress المعقدة في كل مرة. قد تستخدم أيضًا Ingress في بعض البيئات، لكن Route هو عنصر أساسي ومحبوب جدًا في OpenShift.
مثال على Route:
apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: myapp-route
spec:
to:
kind: Service
name: myapp-service
port:
targetPort: 80
tls:
termination: edge
بعد التطبيق:
oc apply -f route.yaml
oc get routes
عندها ستحصل على عنوان URL يمكنك فتحه في المتصفح. إذا كان التطبيق يحتاج إلى HTTPS، يمكنك ضبط TLS حسب سياسة الأمن المطلوبة. وفي بيئات الإنتاج من الأفضل التفكير في نوع التشفير المناسب وشهادة الثقة وطريقة إدارة الشهادات.
الخطوة العاشرة: استخدام oc new-app للتسريع
في كثير من الحالات، لا تريد كتابة كل شيء يدويًا من البداية. OpenShift يوفر أمرًا رائعًا يسمى oc new-app يمكنه تسريع إنشاء التطبيق من صورة أو من مستودع Git. هذا مفيد في التجارب الأولية أو في البيئات السريعة.
مثال على إنشاء تطبيق من مستودع Git:
oc new-app https://github.com/example/myapp.git --name=myapp
أو من صورة جاهزة:
oc new-app registry.example.com/team/myapp:1.0 --name=myapp
بعدها يمكنك إكمال الإعدادات يدويًا أو تعديل الموارد الناتجة. هذه الطريقة توفر وقتًا، لكنها لا تعفيك من فهم ما يحدث تحت الغطاء. من الأخطاء الشائعة أن يعتمد الفريق على oc new-app في كل شيء ثم يواجه صعوبة في التتبع أو إعادة الإنتاج لاحقًا. الأفضل هو استخدامه كأداة تسريع، لا كبديل عن التصميم الواضح.
الخطوة الحادية عشرة: تمرير المتغيرات عبر ConfigMap وSecret
في التطبيقات الواقعية، لا يكفي أن تكتب المتغيرات داخل ملف Deployment. قد يكون لديك إعدادات غير حساسة مثل اسم البيئة أو عنوان الخدمة، وهذه تناسبها ConfigMap. وقد يكون لديك كلمات مرور أو Tokens أو مفاتيح API، وهذه يجب أن تذهب إلى Secret.
مثال ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
MESSAGE: "Hello from ConfigMap"
NODE_ENV: "production"
مثال Secret:
apiVersion: v1
kind: Secret
metadata:
name: myapp-secret
type: Opaque
stringData:
DB_USERNAME: admin
DB_PASSWORD: strongpassword123
ثم داخل الـ Deployment:
envFrom:
- configMapRef:
name: myapp-config
- secretRef:
name: myapp-secret
هذه الطريقة أنظف بكثير من كتابة القيم داخل الصورة أو داخل الكود. كما أنها تجعل التغيير أسهل، لأنك تستطيع تعديل الإعدادات دون إعادة بناء الصورة في كل مرة.
الخطوة الثانية عشرة: التعامل مع الاتصالات إلى قاعدة البيانات
كثير من التطبيقات لا تعمل وحدها، بل تحتاج إلى قاعدة بيانات مثل PostgreSQL أو MySQL أو MongoDB. في هذه الحالة، من الأفضل أن تفصل قاعدة البيانات عن التطبيق، وأن تدير الاتصال عبر Service وSecret وConfigMap. إذا كانت القاعدة خارجية، فقم بتمرير عنوانها واسم المستخدم وكلمة المرور بشكل آمن. وإذا كانت داخل OpenShift، ففكر جيدًا في التخزين الدائم والنسخ الاحتياطي.
مثال متغيرات اتصال بقاعدة بيانات:
env:
- name: DB_HOST
value: "postgres-service"
- name: DB_PORT
value: "5432"
- name: DB_NAME
value: "appdb"
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: myapp-secret
key: DB_USERNAME
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: myapp-secret
key: DB_PASSWORD
هذه الصيغة تساعدك على إبقاء البيانات الحساسة خارج الكود وخارج الصورة، وهو من أساسيات النشر المهني.
الخطوة الثالثة عشرة: التحقق من السجلات والمراقبة
بعد نشر التطبيق، لا تنتهِ القصة. بل تبدأ قصة أخرى أهم: هل التطبيق يعمل فعلًا كما ينبغي؟ هل هناك أخطاء عند الإقلاع؟ هل هناك بطء في الاستجابة؟ هل توجد حاوية تُعاد تشغيلها مرارًا؟ هنا تأتي أهمية السجلات.
استخدم:
oc logs <pod-name>
oc logs -f <pod-name>
واستخدم أيضًا:
oc get events
oc describe deployment myapp
oc describe svc myapp-service
السجلات غالبًا تخبرك بما لن يخبرك به الكود مباشرة. ربما يفشل التطبيق بسبب متغير مفقود، أو بسبب منفذ خاطئ، أو لأن قاعدة البيانات لم تعد متاحة، أو لأن المستخدم داخل الحاوية لا يملك أذونات الكتابة في المسار المطلوب. النظر إلى الـ logs هو عادة أسرع طريق لفهم ما يحدث.
الخطوة الرابعة عشرة: التوسع الأفقي Horizontal Scaling
ميزة قوية في OpenShift هي إمكانية توسيع التطبيق بسهولة عبر زيادة عدد النسخ. إذا كان التطبيق Stateless، فهذا يصبح سهلًا جدًا. يمكنك رفع عدد الـ replicas يدويًا أو تلقائيًا.
تعديل سريع:
oc scale deployment myapp --replicas=4
أو داخل YAML:
spec:
replicas: 4
لكن التوسع لا يعني فقط زيادة العدد. يجب أن يكون التطبيق قادرًا على العمل في بيئة متعددة النسخ من دون مشاكل جلسات أو ملفات محلية أو قفل غير مناسب. إذا كان التطبيق يعتمد على التخزين المحلي داخل الحاوية، فإن زيادة النسخ ستكشف المشكلة فورًا. لذلك التوسع هو اختبار حقيقي لجودة التصميم.
الخطوة الخامسة عشرة: إعداد Autoscaling
يمكنك أيضًا إعداد Horizontal Pod Autoscaler بحيث يزيد أو يقلل عدد الـ Pods بناءً على استهلاك الموارد. هذا يجعل التطبيق أكثر مرونة أمام الضغط المفاجئ.
مثال:
oc autoscale deployment myapp --min=2 --max=6 --cpu-percent=70
أو باستخدام YAML. ولكن تذكّر أن الـ Autoscaling لا يصلح كل شيء. إذا كان التطبيق بطيئًا في الإقلاع أو يعتمد على حالة داخلية غير مناسبة، فلن يعالج HPA هذه المشاكل. هو يساعد في توزيع الحمل، لكنه لا يصلح تصميمًا سيئًا.
الخطوة السادسة عشرة: استراتيجيات التحديث الآمن
من الأشياء الجميلة في OpenShift وKubernetes عمومًا هي إمكانية التحديث التدريجي. بدلًا من إسقاط النسخة القديمة كلها وتشغيل الجديدة دفعة واحدة، يمكنك استخدام Rolling Update بحيث يتم استبدال النسخ تدريجيًا مع الحفاظ على الخدمة.
في الـ Deployment، غالبًا يكون هذا السلوك هو الافتراضي، لكن من الجيد أن تعرف كيف يعمل. عندما تدفع صورة جديدة، يتحرك النظام بشكل محسوب. وإذا تعطل الإصدار الجديد، يمكن أن ترى ذلك بسرعة وتعيد التراجع. التحديثات الآمنة ليست رفاهية، بل ضرورة في أي تطبيق حقيقي.
يمكنك تحديث الصورة هكذا:
oc set image deployment/myapp myapp=registry.example.com/team/myapp:1.1
ثم تراقب:
oc rollout status deployment/myapp
oc rollout history deployment/myapp
وإذا احتجت إلى التراجع:
oc rollout undo deployment/myapp
هذه الأوامر تمنحك راحة كبيرة أثناء العمل، خاصة عندما يكون التطبيق في بيئة إنتاج أو شبه إنتاج.
الخطوة السابعة عشرة: التعامل مع الصلاحيات والأمان
OpenShift معروف بسياساته الأمنية الصارمة نسبيًا. قد يبدو هذا مزعجًا في البداية، لكنه في الحقيقة مفيد جدًا. المنصة تشجع على تشغيل الحاويات كمستخدم غير root، وتقلل من الاعتماد على الصلاحيات العالية. لذلك من المهم أن تختبر تطبيقك مع هذا الواقع.
لو كان تطبيقك يفشل لأنّه يحاول الكتابة في /root أو لأنّه يتوقع أن يكون root، فهذه إشارة إلى أن الصورة تحتاج إلى إعادة تصميم. من الأفضل أن تهيئ التطبيق ليستخدم مسارًا مثل /tmp أو /app/data مع أذونات مناسبة، وأن تتأكد من أن أي ملف مطلوب للكتابة متاح للمستخدم غير root.
في بعض الحالات ستحتاج إلى ضبط Security Context أو استخدام ملفات Manifest مناسبة لسياسات المشروع. لكن الفكرة الأساسية يجب أن تبقى واضحة: لا تجعل نجاح التطبيق معتمدًا على root.
الخطوة الثامنة عشرة: مثال تطبيقي كامل من البداية إلى النهاية
لنجمع الصورة الآن في مثال شبه كامل. تخيل أن لديك تطبيق Node.js بسيط. لديك server.js و Dockerfile وملفات YAML. ستبدأ ببناء الصورة:
docker build -t registry.example.com/team/myapp:1.0 .
docker push registry.example.com/team/myapp:1.0
ثم تنشئ المشروع:
oc new-project myapp-dev
ثم تطبق ملف الـ Deployment:
oc apply -f deployment.yaml
ثم الخدمة:
oc apply -f service.yaml
ثم الـ Route:
oc apply -f route.yaml
بعدها تتحقق من الموارد:
oc get all
oc get pods
oc get svc
oc get routes
وأخيرًا تجرب التطبيق من المتصفح أو باستخدام curl:
curl http://$(oc get route myapp-route -o jsonpath='{.spec.host}')
إذا ظهر لك الرد المتوقع، فمعنى ذلك أن الدورة كاملة قد اكتملت بنجاح: بناء، نشر، خدمة، وتوجيه خارجي.
الخطوة التاسعة عشرة: نصائح عملية من واقع التجربة
من أكثر الأشياء التي يتجاهلها المبتدئون أن النشر الجيد يبدأ من التطبيق نفسه. لا تنتظر حتى تصل إلى OpenShift ثم تكتشف أن التطبيق لا يقرأ متغيرات البيئة، أو أن المنافذ غير مرنة، أو أن التهيئة محصورة في ملف محلي لا يمكن استبداله. اجعل التطبيق منذ البداية قابلًا للتشغيل في بيئة حاويات. هذه النصيحة تبدو بسيطة، لكنها تختصر الكثير.
كما أن من المهم جدًا أن تعتمد أسماء واضحة للموارد. سمِّ الـ Deployment والـ Service والـ Route بطريقة تعكس وظيفة التطبيق والبيئة. في المشاريع الكبيرة، الأسماء الواضحة توفر وقتًا هائلًا في التتبع والصيانة. كذلك اجعل صورك الحاوية مرقمة بإصدارات واضحة مثل 1.0.0 و1.0.1 بدل الاكتفاء بـ latest. الاعتماد على latest قد يسبب ارتباكًا كبيرًا عند التحديث أو الرجوع إلى الخلف.
ولا تنس التوثيق الداخلي. قد تعرف أنت اليوم لماذا استُخدم هذا المنفذ أو لماذا وُضع هذا الـ Probe، لكن بعد أسبوع أو شهر سيأتي شخص آخر إلى المشروع. وجود تعليقات واضحة أو README جيد يجعل المشروع أكثر احترافية بكثير.
الخطوة العشرون: أخطاء شائعة يجب تجنبها
هناك مجموعة من الأخطاء التي تتكرر باستمرار. أولها تشغيل التطبيق على localhost داخل الحاوية بدل 0.0.0.0. ثانيها وضع الأسرار داخل الكود أو داخل الصورة. ثالثها إهمال الـ probes. رابعها عدم ضبط المنافذ بشكل صحيح بين التطبيق والـ Service. خامسها نسيان أن OpenShift قد يشغّل الحاوية بصلاحيات أقل مما تتوقع. وسادسها استخدام صورة غير مهيأة للعمل في بيئة إنتاج حقيقية.
خطأ شائع آخر هو بناء الصورة داخل كل بيئة بشكل غير منظم. الأفضل أن يكون عندك خط CI/CD واضح: اختبار، بناء، فحص، دفع إلى Registry، ثم نشر إلى OpenShift. كلما كانت العملية أكثر آلية، كانت أقل عرضة للأخطاء اليدوية.
الخطوة الحادية والعشرون: دمج CI/CD مع OpenShift
واحدة من أقوى نقاط OpenShift هي أنه ينسجم جيدًا مع مسارات الـ CI/CD. يمكنك أن تجعل كل push إلى GitHub أو GitLab يمر عبر pipeline تقوم ببناء الصورة، وتشغيل الاختبارات، وتحليل الجودة، ثم نشر النسخة المناسبة إلى البيئة المطلوبة. بهذه الطريقة يصبح النشر متكررًا، واضحًا، وقابلًا للتدقيق.
مثال مبسط لفكرة pipeline باستخدام أداة CI عامة:
stages:
- test
- build
- deploy
test:
stage: test
script:
- npm install
- npm test
build:
stage: build
script:
- docker build -t registry.example.com/team/myapp:$CI_COMMIT_SHA .
- docker push registry.example.com/team/myapp:$CI_COMMIT_SHA
deploy:
stage: deploy
script:
- oc login $OPENSHIFT_API --token=$OPENSHIFT_TOKEN
- oc project myapp-dev
- oc set image deployment/myapp myapp=registry.example.com/team/myapp:$CI_COMMIT_SHA
- oc rollout status deployment/myapp
هذا المثال مبسّط، لكنه يوضح الفكرة الجوهرية: النشر يجب أن يكون آليًا وقابلًا للتكرار. كلما قلّ التغيير اليدوي في الإنتاج، زادت الثقة في المنصة.
الخطوة الثانية والعشرون: الفرق بين التطوير المحلي والإنتاج
من الأخطاء الفكرية الشائعة أن نفترض أن ما يعمل محليًا سيعمل كما هو في OpenShift. الواقع مختلف قليلاً. في جهازك المحلي قد تكون لديك صلاحيات كاملة، وشبكة مفتوحة، وملفات موجودة في أماكن معينة، بينما في OpenShift قد تكون البيئة مقيدة، والاتصال محدودًا، والمستخدم غير root، والسياسات الأمنية مختلفة. لذلك لا تتعامل مع الإنتاج كأنه نسخة أكبر من جهازك فقط، بل كبيئة تختلف في السلوك والقيود.
هذا لا يعني أن التعقيد كبير لدرجة الخوف، بل يعني فقط أن النشر الناجح هو نشر يفكر في القيود من البداية. حين تكتب التطبيق بهذه العقلية، ستجد أن النقل إلى OpenShift يصبح طبيعيًا جدًا بدل أن يكون صدمة.
الخطوة الثالثة والعشرون: مثال على تطبيق Python بسيط
حتى تكتمل الصورة، إليك مثالًا سريعًا لتطبيق Python باستخدام Flask:
from flask import Flask
import os
app = Flask(__name__)
@app.route("/")
def home():
message = os.getenv("MESSAGE", "Hello from OpenShift")
return f"<h1>{message}</h1>"
@app.route("/health")
def health():
return {"status": "ok"}, 200
if __name__ == "__main__":
port = int(os.getenv("PORT", 5000))
app.run(host="0.0.0.0", port=port)
و Dockerfile:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
مرة أخرى، لاحظ أننا نستخدم 0.0.0.0، ونفصل الإعدادات في متغيرات بيئة، ونضيف endpoint للـ health. هذه التفاصيل الصغيرة هي التي تصنع الفرق بين تطبيق يبدو جيدًا وتطبيق يعمل بثقة على منصة حقيقية.
الخطوة الرابعة والعشرون: متى تستخدم BuildConfig داخل OpenShift؟
في بعض الفرق، يكون من المفيد أن يقوم OpenShift نفسه ببناء الصورة من المستودع. هنا يأتي دور BuildConfig. هذا مفيد عندما تريد ربط الكود مباشرة بالمنصة وتقليل عدد الأدوات الخارجية. لكن في كثير من البيئات الحديثة، يفضّل البعض أن تتم عملية البناء في الـ CI/CD ثم تُدفع الصورة إلى Registry، لأن ذلك يمنحهم تحكمًا أكبر وشفافية أفضل.
إذا اخترت BuildConfig، فستعرف أنك تدخل في مسار أعمق من مجرد النشر، لأنك ستدير دورة البناء داخل المنصة أيضًا. أما إذا اخترت CI/CD خارجي، فسيكون OpenShift في الغالب مسؤولًا عن التشغيل والنشر، بينما تتكفل المنظومة الخارجية بالبناء والتحقق. كل نمط له مكانه، ولا يوجد جواب واحد يناسب الجميع.
الخطوة الخامسة والعشرون: التعامل مع التخزين الدائم
إذا كان تطبيقك يحتاج إلى حفظ ملفات، فلا تضعها داخل النظام المحلي للحاوية فقط. لأن الحاوية قد تُعاد إنشاؤها في أي لحظة، وستفقد البيانات. بدلًا من ذلك استخدم Persistent Volume وPersistent Volume Claim. هذه نقطة حيوية في التطبيقات التي تتعامل مع رفع ملفات أو صور أو سجلات أو بيانات معالجة تحتاج إلى بقاء.
الفكرة هنا ليست مجرد إضافة Volume، بل فهم طبيعة البيانات. هل هي بيانات يمكن إعادة توليدها بسهولة؟ أم بيانات يجب أن تبقى؟ هذا السؤال يحدد التصميم منذ البداية.
الخطوة السادسة والعشرون: لماذا يحب المطورون OpenShift؟
المطورون يحبون OpenShift عندما يشعرون أن المنصة لا تعرقلهم، بل تنظّمهم. هم يحبون أن يدفعوا الكود بسرعة، وأن يروا التطبيق يعمل، وأن يملكوا وسيلة واضحة للتراجع عند الخطأ، وأن يمروا عبر مسار نشر مفهوم. كما يحبون أن تكون البيئة قريبة من الواقع، بحيث لا يفاجَؤون عند الانتقال إلى الإنتاج.
وأحيانًا تكون أفضل ميزة في OpenShift هي أنه يفرض عليك الانضباط. قد يبدو هذا مزعجًا في أول الطريق، لكن بعد فترة تكتشف أن هذا الانضباط هو ما يجعل التطبيقات أكثر استقرارًا، والفرق أكثر إنتاجية، والمشكلات أسهل في التشخيص.
خاتمة
نشر التطبيقات على OpenShift خطوة بخطوة ليس مهمة معقدة إذا تعاملت معها كرحلة مترابطة لا كأوامر متفرقة. ابدأ بتجهيز التطبيق ليكون مناسبًا للحاويات، ثم ابنِ الصورة بشكل نظيف، ثم أنشئ المشروع والـ Deployment والـ Service والـ Route، ثم أضف ConfigMap وSecret والـ Probes والـ Resources، وبعدها اختبر السجلات، وراقب الأداء، وفعّل التوسع، وأدخل النشر ضمن CI/CD. هكذا يتحول OpenShift من منصة تبدو كبيرة ومربكة إلى بيئة عمل واضحة ومفيدة جدًا.
السر الحقيقي ليس في حفظ الأوامر، بل في فهم العلاقة بين الكود والحاوية والـ Cluster. عندما تفهم هذه العلاقة، تصبح أي عملية نشر أكثر سلاسة، وأي خطأ أسهل في الإصلاح، وأي تحديث أكثر أمانًا. وهذا بالضبط ما يجعل OpenShift خيارًا قويًا للمؤسسات والفرق التي تريد أن تنشر بثقة، وتُدير تطبيقاتها بطريقة احترافية، وتبني مسارًا مستدامًا من التطوير إلى الإنتاج.