السلسلة: هذا الدرس جزء من سلسلة شهادة CKAD. للحصول على نظرة شاملة، اقرأ المقال الرئيسي أولاً.
مقدمة
المجال 3 «Application Environment, Configuration and Security» يزن 25% من امتحان CKAD — أكثف جزء في المنهج. كذلك المجال الذي يخسر فيه معظم المرشحين نقاطاً بجهل دقائق: الفرق بين حقن ConfigMap عبر متغيّر بيئة وعبر ملف مربوط، base64 مقابل تشفير حقيقي للأسرار، ServiceAccount الافتراضي مقابل مخصّص، requests مقابل limits، ResourceQuota على مستوى namespace مقابل LimitRange. هذا الدرس يدرّب كل مفهوم بـ manifests مُختبَرة على عنقود kind 1.35.
المتطلبات
- عنقود kind 1.35 شغّال
- إكمال الدروس السابقة من السلسلة
- 40 دقيقة
الخطوة 1 — إنشاء ConfigMap من ملف
ConfigMap يخزّن بيانات إعداد غير سرية: URLs API، مستويات log، feature flags، معاملات runtime. ثلاث طرق للإنشاء: --from-literal، --from-file، أو manifest YAML مباشر.
cat > app.properties <<EOF
LOG_LEVEL=info
API_BASE_URL=https://api.example.com
FEATURE_FLAGS=new_ui,fast_search
EOF
k create configmap app-config --from-file=app.properties $do > cm.yaml
k apply -f cm.yaml
k get configmap app-config -o yaml | head -20
مخرَج YAML يُظهر البيانات في قسم data:، مع مفتاح app.properties يحتوي كامل الملف كقيمة. هذا تماماً ما يُربط حين تشير إلى هذا ConfigMap بصفته ملفاً في Pod.
الخطوة 2 — حقن ConfigMap عبر متغيّر بيئة
الحقن عبر env هو الطريقة الأبسط: كل مفتاح في ConfigMap يصير متغيّر بيئة للحاوية. القيد: التغييرات لا تُروَّج ديناميكياً — يلزم إعادة تشغيل الـ Pod.
k create configmap envs-cm --from-literal=APP_ENV=production --from-literal=DEBUG=false
cat > pod-env.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: env-consumer
spec:
containers:
- name: app
image: busybox:1.37
command: ["sh","-c","env | grep -E 'APP_ENV|DEBUG'; sleep 3600"]
envFrom:
- configMapRef:
name: envs-cm
EOF
k apply -f pod-env.yaml
sleep 5
k logs env-consumer | head -5
المخرَج يعرض APP_ENV=production وDEBUG=false. envFrom يحقن كل مفاتيح ConfigMap دفعةً. لأخذ مفتاح واحد، استخدم env.valueFrom.configMapKeyRef.
الخطوة 3 — حقن ConfigMap عبر ملف مربوط
الحقن عبر ملف مربوط يعامل ConfigMap كـ volume: كل مفتاح يصير ملفاً في المجلد المربوط. ميزة حاسمة: تغييرات ConfigMap تُروَّج في الـ volume دون إعادة تشغيل الـ Pod (بتأخير عشرات الثواني مرتبط بـ kubelet sync).
cat > pod-mount.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: file-consumer
spec:
volumes:
- name: config-vol
configMap:
name: app-config
containers:
- name: app
image: busybox:1.37
command: ["sh","-c","cat /etc/conf/app.properties; sleep 3600"]
volumeMounts:
- name: config-vol
mountPath: /etc/conf
EOF
k apply -f pod-mount.yaml
sleep 5
k logs file-consumer
محتوى ملف app.properties يظهر في السجلات. هذه الطريقة مفضّلة لملفات إعداد كاملة (nginx.conf، my.cnf، application.yml) لا لمتغيّرات فردية.
الخطوة 4 — إنشاء Secret وفهم ترميز base64
Secrets تخزّن بيانات حساسة. نقطة حاسمة في الامتحان: Secrets مُرمَّزة base64، ليست مشفَّرة. أي شخص لديه وصول إلى etcd أو API Kubernetes يستطيع فكّ ترميز السر. التشفير at-rest في etcd ميزة منفصلة تُفعَّل يدوياً بـ EncryptionConfiguration.
k create secret generic db-creds \
--from-literal=username=admin \
--from-literal=password=Tr0ub4dor
k get secret db-creds -o yaml
echo "YWRtaW4=" | base64 -d # admin
echo "VHIwdWI0ZG9y" | base64 -d # Tr0ub4dor
ترى بوضوح username وpassword بالنص الصريح بعد فكّ base64. هذه التوضيح يأتي منهجياً في الامتحان كسؤال فخّ: «هل Secrets مشفَّرة؟» — الجواب الصحيح «لا، فقط مُرمَّزة base64؛ التشفير at-rest يجب ضبطه منفصلاً».
الخطوة 5 — حقن Secret كمتغيّر بيئة
حقن Secret يتبع نفس منطق ConfigMap. ممارسة شائعة: كلمة سرّ قاعدة البيانات تُحقَن في تطبيق عبر env.
cat > pod-secret.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: db-app
spec:
containers:
- name: app
image: busybox:1.37
command: ["sh","-c","echo User=$DB_USER; echo Pass=$DB_PASS; sleep 3600"]
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-creds
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: db-creds
key: password
EOF
k apply -f pod-secret.yaml
sleep 5
k logs db-app
المخرَج يعرض User=admin وPass=Tr0ub4dor — الأسرار مُحقَنة بالنص الصريح في الحاوية. هكذا تستهلكها التطبيقات. الأمان يعتمد على تقييد الوصول للـ Pod وعلى تشفير at-rest في etcd.
الخطوة 6 — ServiceAccount مخصّص وtoken automounted
كل Pod يشتغل بـ ServiceAccount، افتراضياً default. لـ Pod يتفاعل مع API Kubernetes (مثل controller، job رصد)، أنشئ ServiceAccount مخصّصاً بأدنى الصلاحيات الضرورية.
k create serviceaccount monitor-sa
k create role pod-reader --verb=get,list,watch --resource=pods
k create rolebinding monitor-sa-binding --role=pod-reader --serviceaccount=default:monitor-sa
cat > pod-sa.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: monitor-pod
spec:
serviceAccountName: monitor-sa
containers:
- name: monitor
image: bitnami/kubectl:1.35
command: ["sh","-c","kubectl get pods; sleep 3600"]
EOF
k apply -f pod-sa.yaml
sleep 5
k logs monitor-pod | head -10
الـ Pod يدرج pods في namespace default لأن ServiceAccount له صلاحية get pods. إن جرّبت kubectl get nodes داخل هذا الـ Pod، تحصل على خطأ 403 Forbidden — مثبتاً أن الصلاحيات مُحدّدة المجال بصرامة.
الخطوة 7 — ضبط requests وlimits وResourceQuota
requests تضمن حجز موارد على العقدة. limits تعرّف السقف — التجاوز يطلق OOMKill (RAM) أو throttling (CPU). ResourceQuota يحدّ الموارد الكلية لـ namespace.
k create namespace dev-team
k create quota dev-quota --hard=cpu=2,memory=2Gi,pods=10 -n dev-team
cat > pod-resources.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: app-with-limits
namespace: dev-team
spec:
containers:
- name: app
image: nginx:1.27
resources:
requests:
cpu: "100m"
memory: "64Mi"
limits:
cpu: "500m"
memory: "128Mi"
EOF
k apply -f pod-resources.yaml
k describe quota dev-quota -n dev-team
k describe pod app-with-limits -n dev-team | grep -A 5 Limits
مخرَج describe quota يُظهر الاستهلاك الحالي («used: cpu=100m, memory=64Mi, pods=1») مقابل السقف. أي Pod يُنشأ في هذا namespace يجب أن يعلن resources، وإلا يرفضه admission webhook.
الخطوة 8 — SecurityContext للتحصين
SecurityContext يطبّق قيود Linux على الحاوية: مستخدم غير root، إسقاط capabilities Linux، نظام ملفات للقراءة فقط، منع تصعيد الامتيازات. المجال 3c من المنهج.
cat > pod-secure.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
containers:
- name: app
image: busybox:1.37
command: ["sh","-c","id; ls -la /; sleep 3600"]
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: true
EOF
k apply -f pod-secure.yaml
sleep 5
k logs secure-app | head -5
مخرَج id يجب أن يعرض uid=1000. نظام الملفات read-only، يمنع أي malware من تعديل الثنائي التطبيقي. هذه الوضعية الأمنية الموصى بها بـ Pod Security Standards Restricted.
فهم env مقابل envFrom مقابل volumeMounts
ثلاث طرق لحقن إعداد في حاوية، لكل واحدة حالات استخدامها. env يحقن متغيّراً فردياً يشير إلى مفتاح محدّد في ConfigMap أو Secret. مطوَّل لكنه صريح. envFrom يحقن كل مفاتيح ConfigMap/Secret في توجيه واحد. عملي لكن مبهم — مطوّر جديد يجب أن يقرأ ConfigMap لمعرفة ما يُكشَف. volumeMounts يربط ConfigMap/Secret كشجرة ملفات. مفضّل لملفات config كاملة ويسمح بترويج التعديلات ديناميكياً.
قاعدة عملية CKAD: env لـ 1-3 متغيّرات حرجة (DATABASE_URL، API_KEY)، envFrom لمجموعات متغيّرات بيئة، volumeMounts لملفات nginx.conf، application.yml، settings.json.
أخطاء شائعة
| الخطأ | السبب | الحل |
|---|---|---|
| Pod يرفض التشغيل مع runAsNonRoot | صورة الحاوية تشتغل root افتراضياً | اختر صورة تحدّد USER غير root، أو عدّل Dockerfile |
| Secret غير موجود | اختلاف namespace | Secret يجب أن يكون في نفس namespace كـ Pod |
| ResourceQuota يحجب الإنشاء | Pod بلا requests/limits صريحة | أعلن دائماً resources في manifests إن كان quota نشطاً |
| Permission denied على ServiceAccount | RoleBinding مفقودة أو namespace خاطئ | kubectl auth can-i --as=system:serviceaccount:NS:NAME VERB RESOURCE |
| تحديث ConfigMap لا يُروَّج | حقن عبر env | أعد نشر Pod (kubectl rollout restart) أو استخدم volumeMount |
دروس مرافقة
- Pods وDeployments وJobs وأنماط متعددة الحاويات CKAD
- تركيب عنقود Kubernetes CKAD بـ kind
- العودة إلى المقال الرئيسي CKAD
أسئلة شائعة
هل أنشئ دائماً ServiceAccount مخصّصاً؟
لـ Pods التي لا تتفاعل مع API Kubernetes: لا، default يكفي. لـ Pods controllers أو jobs إدارية أو agents: نعم، أنشئ SA مخصّصاً بأدنى الصلاحيات.
كيف نشفّر Secrets at-rest؟
اضبط EncryptionConfiguration في kube-apiserver مع مزوّد AES-CBC أو KMS. خارج نطاق CKAD لكنه أساسي في الإنتاج.