تمرين عملي شامل: نشر مشروع OrderFlow
النشر هو الميل الأخير—أتمتة CI/CD وتنسيق K8s والمراقبة والتنبيهات تضمن رحلة سلسة للتطبيقات من التطوير إلى الإنتاج.
1. ما ستتعلمه
- خط أنابيب CI/CD: بناء الصور باستخدام GitHub Actions ← الدفع إلى السجل ← النشر المتداول إلى K8s
- نشر K8s للإنتاج: Deployment + HPA للتحجيم التلقائي + بوابة Ingress
- نشر القابلية للرصد: Prometheus + Grafana + قواعد تنبيه AlertManager
- تعزيز أمان الإنتاج: إدارة Secrets / NetworkPolicy / معايير أمان Pod
- Bob نشر OrderFlow في بيئة الإنتاج لدعم عمليات أعمال تتعامل مع ملايين الطلبات يوميًا
2. قصة حقيقية لمهندس DevOps
(1) نقطة الألم: النشر اليدوي كالوقوف على لغم
نشر Bob يدويًا OrderFlow إلى الإنتاج: محليًا mvn package ← docker build ← دفع الصورة ← kubectl apply ← فحص حالة الصحة. العملية بالكامل تستغرق 30 دقيقة وعرضة للأخطاء: نسيان تحديث التهيئة، دفع إصدار صورة خاطئ، أو عدم إيقاف Pods القديمة بأمان أثناء التحديثات المتداولة، مما يؤدي إلى أخطاء 502.
(2) حلول CI/CD
خدمة شاملة لخطوط الإنتاج المؤتمتة:
graph LR
A["Git Push"] --> B["GitHub Actions<br/>بناء + اختبار"]
B --> C["Docker Build<br/>+ دفع إلى السجل"]
C --> D["نشر K8s<br/>المتداول"]
D --> E["فحص الصحة<br/>+ اختبار دخاني"]
E --> F["المراقبة<br/>Grafana"]
(3) الإيرادات
بعد أن أنشأ Bob خط CI/CD، أدى دفع الكود إلى نشر تلقائي، وإتمام العملية من الكود إلى الإنتاج في 5 دقائق. مع HPA يتولى التحجيم التلقائي وPrometheus يتولى التنبيهات التلقائية، تحول Bob من "رجل إطفاء الحرائق" إلى "مراقب".
3. خط أنابيب CI/CD
(1) ▶ مثال: سير عمل GitHub Actions
# .github/workflows/deploy.yml
name: OrderFlow CI/CD
on:
push:
branches: [main]
tags: ['v*']
env:
REGISTRY: registry.example.com
IMAGE_NAME: orderflow-service
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Run tests
run: mvn verify -B
- name: Build Docker image
run: docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} .
- name: Login to Registry
run: echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u ${{ secrets.REGISTRY_USERNAME }} --password-stdin
- name: Push image
run: docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
deploy:
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v4
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/orderflow \
orderflow=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-n orderflow
kubectl rollout status deployment/orderflow -n orderflow --timeout=300s
- name: Smoke test
run: |
sleep 10
curl -sf https://api.orderflow.example.com/actuator/health | grep '"status":"UP"'
الناتج:
CONTAINER ID IMAGE STATUS PORTS
abc123 nginx:latest Up 2 hours 0.0.0.0:80->80/tcp
| مرحلة الخط | العملية | معالجة الفشل |
|---|---|---|
| بناء + اختبار | mvn verify |
حظر النشر |
| Docker Build | بناء صورة متعددة المراحل | حظر النشر |
| دفع إلى السجل | دفع إلى مستودع الصور | إعادة المحاولة 3 مرات |
| نشر K8s | kubectl set image + rollout |
تراجع تلقائي |
| اختبار دخاني | التحقق من فحص الصحة | تراجع + إشعار |
4. نشر K8s للإنتاج
(1) ▶ مثال: نشر إنتاجي + HPA
apiVersion: apps/v1
kind: Deployment
metadata:
name: orderflow
namespace: orderflow
spec:
replicas: 3
selector:
matchLabels: { app: orderflow }
strategy:
rollingUpdate: { maxUnavailable: 0, maxSurge: 1 }
template:
metadata:
labels: { app: orderflow }
spec:
terminationGracePeriodSeconds: 60
containers:
- name: orderflow
image: registry.example.com/orderflow-service:latest
ports:
- { containerPort: 8080 }
- { containerPort: 8081 }
envFrom:
- configMapRef: { name: orderflow-config }
- secretRef: { name: orderflow-secrets }
resources:
requests: { memory: "512Mi", cpu: "250m" }
limits: { memory: "1Gi", cpu: "1000m" }
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 10"]
livenessProbe:
httpGet: { path: /actuator/health/liveness, port: 8080 }
initialDelaySeconds: 60
periodSeconds: 30
readinessProbe:
httpGet: { path: /actuator/health/readiness, port: 8080 }
initialDelaySeconds: 30
periodSeconds: 10
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: orderflow-hpa
namespace: orderflow
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: orderflow
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
الناتج:
Deployment.apps/my-app created
Service/my-app-service exposed
Ingress/my-app-ingress created
| تهيئة K8s للإنتاج | القيمة | السبب |
|---|---|---|
maxUnavailable: 0 |
بدون توقف | الحفاظ دائمًا على عدد النسخ المستهدف |
terminationGracePeriodSeconds: 60 |
إيقاف أنيق | انتظار تصريف الطلبات |
preStop: sleep 10 |
إنهاء مؤجل | يستمر K8s في تصريف حركة المرور حتى بعد الإزالة من Service |
resources.requests/limits |
تهيئة QoS | ضمان تخصيص الموارد + تحديد التجاوزات |
(2) ▶ مثال: Ingress + TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: orderflow-ingress
namespace: orderflow
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts: [api.orderflow.example.com]
secretName: orderflow-tls
rules:
- host: api.orderflow.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service: { name: orderflow, port: { number: 80 } }
الناتج:
Deployment.apps/my-app created
Service/my-app-service exposed
Ingress/my-app-ingress created
5. إطلاق القابلية للرصد
(1) ▶ مثال: Prometheus + AlertManager
# prometheus.yml
scrape_configs:
- job_name: orderflow
metrics_path: /actuator/prometheus
scrape_interval: 15s
kubernetes_sd_configs:
- role: pod
namespaces:
names: [orderflow]
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
action: keep
regex: orderflow
الناتج:
Deployment.apps/my-app created
Service/my-app-service exposed
Ingress/my-app-ingress created
# alert_rules.yml
groups:
- name: orderflow-slo
rules:
- alert: SLOErrorRateExceeded
expr: |
sum(rate(http_server_requests_seconds_total{namespace="orderflow",status=~"5.."}[5m]))
/ sum(rate(http_server_requests_seconds_total{namespace="orderflow"}[5m])) > 0.001
for: 5m
labels: { severity: critical }
annotations:
summary: "معدل خطأ OrderFlow يتجاوز SLO (0.1%)"
- alert: SLOLatencyExceeded
expr: |
histogram_quantile(0.99,
sum(rate(http_server_requests_seconds_bucket{namespace="orderflow"}[5m])) by (le))
> 0.1
for: 5m
labels: { severity: warning }
annotations:
summary: "زمن استجابة P99 لـ OrderFlow يتجاوز SLO (100ms)"
(2) ▶ مثال: مقاييس لوحة SLO في Grafana
| اللوحة | PromQL | هدف SLO |
|---|---|---|
| زمن استجابة P99 | histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket{namespace="orderflow"}[5m])) by (le)) |
< 100 مللي ثانية |
| معدل الخطأ | sum(rate(http_server_requests_seconds_total{namespace="orderflow",status=~"5.."}[5m])) / sum(rate(http_server_requests_seconds_total{namespace="orderflow"}[5m])) |
< 0.1% |
| التوفر | 1 - (sum(rate(http_server_requests_seconds_total{namespace="orderflow",status=~"5.."}[5m])) / sum(rate(http_server_requests_seconds_total{namespace="orderflow"}[5m]))) |
> 99.9% |
| إنتاجية الطلبات | rate(orderflow_orders_created_total[1m]) * 60 |
> 100/دقيقة |
6. تعزيزات أمان الإنتاج
(1) ▶ مثال: NetworkPolicy + أمان Pod
# NetworkPolicy: تقييد الوصول
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: orderflow-netpol
namespace: orderflow
spec:
podSelector:
matchLabels: { app: orderflow }
policyTypes: [Ingress]
ingress:
- from:
- namespaceSelector:
matchLabels: { name: ingress-nginx }
ports:
- { port: 8080, protocol: TCP }
- from:
- namespaceSelector:
matchLabels: { name: monitoring }
ports:
- { port: 8081, protocol: TCP }
---
# أمان Pod: التشغيل كمستخدم غير جذر
apiVersion: v1
kind: LimitRange
metadata:
name: orderflow-limits
namespace: orderflow
spec:
limits:
- type: Container
default:
memory: "1Gi"
cpu: "500m"
defaultRequest:
memory: "256Mi"
cpu: "100m"
الناتج:
Deployment.apps/my-app created
Service/my-app-service exposed
Ingress/my-app-ingress created
| عنصر تعزيز الأمان | الإجراء | الوصف |
|---|---|---|
| التشغيل غير الجذر | Dockerfile USER appuser |
منع هروب الحاوية |
| NetworkPolicy | تقييد مصادر الدخول | السماح فقط لـ Ingress والمراقبة |
| تشفير Secret | تهيئة تشفير etcd | منع تخزين Secrets بنص عادي |
| TLS | شهادات cert-manager التلقائية | نقل مشفر |
| حدود الموارد | LimitRange | منع تنازع الموارد |
7. مثال شامل: قائمة نشر OrderFlow الكاملة
# k8s/orderflow-complete.yaml
---
apiVersion: v1
kind: Namespace
metadata:
name: orderflow
labels: { name: orderflow }
---
apiVersion: v1
kind: ConfigMap
metadata: { name: orderflow-config, namespace: orderflow }
data:
SPRING_PROFILES_ACTIVE: "prod"
DB_HOST: "mysql.orderflow.svc.cluster.local"
REDIS_HOST: "redis.orderflow.svc.cluster.local"
MANAGEMENT_SERVER_PORT: "8081"
---
apiVersion: v1
kind: Secret
metadata: { name: orderflow-secrets, namespace: orderflow }
type: Opaque
data:
DB_PASSWORD: <base64-encoded>
REDIS_PASSWORD: <base64-encoded>
JWT_PRIVATE_KEY: <base64-encoded>
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: orderflow, namespace: orderflow }
spec:
replicas: 3
selector: { matchLabels: { app: orderflow } }
strategy: { rollingUpdate: { maxUnavailable: 0, maxSurge: 1 } }
template:
metadata: { labels: { app: orderflow } }
spec:
terminationGracePeriodSeconds: 60
containers:
- name: orderflow
image: registry.example.com/orderflow-service:1.0.0
ports: [{ containerPort: 8080 }, { containerPort: 8081 }]
envFrom:
- { configMapRef: { name: orderflow-config } }
- { secretRef: { name: orderflow-secrets } }
resources:
requests: { memory: "512Mi", cpu: "250m" }
limits: { memory: "1Gi", cpu: "1000m" }
lifecycle:
preStop: { exec: { command: ["sh", "-c", "sleep 10"] } }
livenessProbe:
httpGet: { path: /actuator/health/liveness, port: 8080 }
initialDelaySeconds: 60; periodSeconds: 30
readinessProbe:
httpGet: { path: /actuator/health/readiness, port: 8080 }
initialDelaySeconds: 30; periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata: { name: orderflow, namespace: orderflow }
spec:
selector: { app: orderflow }
ports:
- { name: http, port: 80, targetPort: 8080 }
- { name: management, port: 8081, targetPort: 8081 }
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: orderflow-hpa, namespace: orderflow }
spec:
scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: orderflow }
minReplicas: 3
maxReplicas: 10
metrics:
- { type: Resource, resource: { name: cpu, target: { type: Utilization, averageUtilization: 70 } } }
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: orderflow-ingress
namespace: orderflow
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- { hosts: [api.orderflow.example.com], secretName: orderflow-tls }
rules:
- host: api.orderflow.example.com
http:
paths:
- { path: /, pathType: Prefix, backend: { service: { name: orderflow, port: { number: 80 } } } }
❓ أسئلة شائعة
readinessProbe لضمان إضافة Pods إلى Service قبل أن تصبح جاهزة؛ 3) عيّن preStop sleep 10 لمنح K8s وقتًا لتصريف الطلبات بعد إزالة Pod من Service؛ 4) طبق التهيئة server.shutdown=graceful.📖 ملخص
- خط أنابيب CI/CD: دفع الكود ← اختبار آلي ← بناء الصورة ← دفع إلى السجل ← نشر K8s المتداول
- نشر الإنتاج: Deployment + HPA للتحجيم التلقائي + بوابة Ingress + TLS
- نشر بدون توقف: maxUnavailable=0 + readinessProbe + preStop + إيقاف أنيق
- القابلية للرصد: جمع بيانات Prometheus + لوحات SLO في Grafana + تنبيهات AlertManager
- تعزيز الأمان: مستخدمون غير جذر + NetworkPolicy + تشفير Secret + TLS + حدود الموارد
- تسليم شامل من التصميم إلى النشر، يدعم أعمالًا تتعامل مع ملايين الطلبات يوميًا
📝 تمارين
-
مسألة أساسية (الصعوبة: ⭐): اكتب سير عمل GitHub Actions للاختبار التلقائي وبناء صورة Docker بعد دفع الكود.
-
تمرين متقدم (الصعوبة: ⭐⭐): اكتب YAML نشر K8s إنتاجي كامل (Deployment + Service + Ingress + HPA + ConfigMap + Secret) لتنفيذ تحديثات متداولة بدون توقف.
-
تحدٍ (الصعوبة: ⭐⭐⭐): أنشئ نظام قابلية للرصد كامل (Prometheus + Grafana + AlertManager + Jaeger)، وهيئ لوحات SLO وقواعد التنبيه، وحاكِ فشلاً لاختبار التنبيهات وقدرات الاسترداد التلقائي.



