EduFlow

الوحدة 4 · أساسيات Kubernetes

الـ ConfigMaps والـ Secrets والـ Namespaces

20 دقراءة 4 داملأ الفراغات

الدرس في لمحة

انقل إعدادات notes-api إلى ConfigMap وبيانات اعتماده إلى Secret، وضع كل شيء في namespace اسمه notes، وشغّل سير عمل apply وdiff قابلًا للمراجعة من مجلد k8s.

  1. 1‏namespace لـ notes-api
  2. 2الـ ConfigMaps للإعدادات
  3. 3الـ Secrets لبيانات الاعتماد
  4. 4ربطها بـ notes-api
  5. 5سير العمل اليومي

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

حاليًا، الـ Deployment الخاص بـ notes-api مكتوب فيه PORT، والـ Deployment الخاص بقاعدة البيانات فيه كلمة المرور نصًّا صريحًا، وكل شيء يعيش في الـ namespace ‏default بجوار كل ما يجرّبه الآخرون في هذا الـ cluster. هذه ثلاث طرق لإحداث حادث: غيّر إعدادًا فتضطر إلى تعديل حمل العمل؛ اقرأ المستودع فتحصل على كلمة المرور؛ شغّل kubectl delete متهوّرًا في default فتصيب عمل شخص آخر.

‏namespace لـ notes-api

الـ namespace (نطاق الأسماء) نطاق للأسماء. يستطيع فريقان أن يملك كل منهما Deployment اسمه api في namespaces مختلفة. والـ namespaces هي أيضًا حيث تربط الصلاحيات (RBAC) وحصص الموارد، ما يجعلها الوحدة الطبيعية لـ «تطبيق واحد» أو «فريق واحد».

k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: notes

أضف namespace: notes إلى metadata في كل كائن آخر في k8s/، ثم اجعله الافتراضي لهذا الـ context حتى لا تكتب -n notes في كل مرة:

Terminal
kubectl apply -f k8s/namespace.yaml
kubectl config set-context --current --namespace=notes

ما لا تفعله الـ namespaces هو عزل الشبكة. يستطيع Pod في notes الاتصال بـ Service في أي namespace آخر ما لم تقل NetworkPolicy غير ذلك، وتفرضها إضافة الشبكة في الـ cluster لديك. لا تعامل الـ namespace كجدار أمني بمفرده.

الـ ConfigMaps للإعدادات

الـ ConfigMap يحفظ إعدادات مفتاح-قيمة غير حسّاسة:

k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: notes-api-config
  namespace: notes
data:
  PORT: "3000"
  LOG_LEVEL: info

القيم نصوص دائمًا، ولهذا وُضعت "3000" بين علامتَي تنصيص. الآن تستطيع image ‏notes-api نفسها العمل بسجلات info في الإنتاج وdebug في staging، بتطبيق ConfigMap مختلف بدلًا من بناء image مختلفة.

الـ Secrets لبيانات الاعتماد

الـ Secret له شكل الـ ConfigMap نفسه، لكنه مخصّص للقيم الحسّاسة. أنشئه من سطر الأوامر (أو من مدير الأسرار لديك) بدلًا من ملف محفوظ في المستودع:

Terminal
kubectl create secret generic notes-db \
  --from-literal=POSTGRES_PASSWORD='dev-only-change-me' \
  --from-literal=DATABASE_URL='postgres://notes:dev-only-change-me@notes-db:5432/notes'

وهنا الجزء المزعج. انظر إلى ما هو مخزّن:

Terminal
kubectl get secret notes-db -o jsonpath='{.data.POSTGRES_PASSWORD}' | base64 -d

تعود القيمة مباشرة. بيانات الـ Secret مرمّزة بـ base64، لا مشفّرة. ما يجعل الـ Secrets أكثر أمانًا من الـ ConfigMaps هو كل ما يحيط بها: يستطيع RBAC منح صلاحية قراءة الـ ConfigMaps دون الـ Secrets، ويستطيع الـ cluster تشفير الـ Secrets أثناء تخزينها في etcd (والمزوّدون المُدارون يفعلون ذلك عادةً)، ولا تستلم الـ kubelets إلا الـ Secrets الخاصة بالـ Pods المُجدولة على الـ node الخاص بها.

ربطها بـ notes-api

حدّث الـ container في k8s/deployment.yaml:

k8s/deployment.yaml
      containers:
        - name: api
          image: ghcr.io/skylane/notes-api:1.4.0
          ports:
            - name: http
              containerPort: 3000
          envFrom:
            - configMapRef:
                name: notes-api-config
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: notes-db
                  key: DATABASE_URL

الحقل envFrom يحوّل كل مفتاح في الـ ConfigMap إلى متغيّر بيئة. وsecretKeyRef يسحب مفتاحًا واحدًا من الـ Secret. وفي k8s/db.yaml، استبدل القيمة الحرفية لـ POSTGRES_PASSWORD بـ secretKeyRef إلى الـ Secret نفسه.

إذا لم يكن المفتاح المُشار إليه موجودًا، فلن يبدأ الـ container أبدًا. يُظهر الـ Pod الحالة CreateContainerConfigError، ويسمّي kubectl describe pod المفتاح المفقود.

يمكن أيضًا تركيب الـ Secrets والـ ConfigMaps كملفات عبر volume. الملفات المركّبة تتحدّث في الـ Pods العاملة بعد تأخير قصير؛ أما متغيّرات البيئة فثابتة منذ بدء الـ container. بعد تغيير إعدادات تصل عبر متغيّرات البيئة، أعِد تشغيل الـ Pods تدريجيًا:

Terminal
kubectl rollout restart deployment/notes-api

سير العمل اليومي

مجلد k8s/ لديك يحتوي الآن على namespace.yaml وconfigmap.yaml وdb.yaml وdeployment.yaml وservice.yaml. والحلقة لكل تغيير هي نفسها:

Terminal
kubectl diff -f k8s/
kubectl apply -f k8s/
kubectl get all -l app.kubernetes.io/name=notes-api

الأمر diff هو خطوة المراجعة: يُظهر ما سيتغيّر على الـ cluster الحيّ، حقلًا بحقل، قبل أن تغيّره. اجعله عادة وستلتقط التعديل الشارد الذي كان سيعيد ضبط إصلاح طارئ أجراه أحدهم. وحين تحتاج إلى قيم مختلفة لكل بيئة، فإن Kustomize (المدمج في kubectl كـ kubectl apply -k) يضع طبقات صغيرة (overlays) فوق هذه الملفات الأساسية؛ وHelm، في الدرس 5.3، هو الإجابة الشائعة الأخرى.

يعمل notes-api الآن على Kubernetes بشبكة وإعدادات سليمة. لكنه ليس جاهزًا للإنتاج بعد: لا يعرف Kubernetes هل هو سليم، ولا كم يحتاج من المعالج، ولا كيف يحدّثه بأمان. القسم الخامس يغطّي الثلاثة.

أهم النقاط

  • الـ ConfigMaps تحفظ الإعدادات غير الحسّاسة؛ والـ Secrets تحفظ بيانات الاعتماد، وتُحقن كمتغيّرات بيئة أو كملفات مركّبة.
  • قيم الـ Secret مرمّزة بـ base64 فقط، لا مشفّرة؛ احمِها بـ RBAC، وبالتشفير أثناء التخزين، وبإبقائها خارج Git.
  • متغيّرات البيئة القادمة من ConfigMap أو Secret تُقرأ مرة واحدة عند بدء الـ container، لذا أعِد تشغيل الـ Deployment بعد تغييرها.
  • الـ Namespaces تحدّد نطاق الأسماء والصلاحيات والحصص لفريق أو تطبيق، لكنها لا تعزل حركة الشبكة وحدها.
  • تشغيل kubectl diff -f k8s/ قبل kubectl apply -f k8s/ يُظهر بالضبط ما سيتغيّر، مثل pull request للـ cluster.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

يحفظ زميلك في Git ملف manifest لـ Secret قيم data فيه نصوص base64، قائلًا «لا بأس، إنها مرمّزة». ما المشكلة؟

السؤال 2 من 4

تغيّر LOG_LEVEL في الـ ConfigMap ‏notes-api-config وتطبّقه. الـ Pods تستهلكه عبر envFrom. ماذا يحدث؟

السؤال 3 من 4

‏Pod جديد عالق بحالة CreateContainerConfigError. ما السبب الأرجح؟

السؤال 4 من 4

‏notes-api وخدمة مدفوعات يعملان في namespaces منفصلة على الـ cluster نفسه. هل يستطيع Pod من notes-api فتح اتصال بالـ Service الخاص بقاعدة بيانات المدفوعات؟

تدريب

املأ الفراغات

‏Namespace، ثم Secret، ثم إعادة تشغيل

املأ الفراغات لإنشاء الـ namespace ‏notes وجعله الافتراضي، وتخزين كلمة مرور قاعدة البيانات في Secret، وفكّ ترميزها لإثبات أنها base64 فقط، وإعادة تشغيل notes-api حتى يلتقط متغيّرات البيئة المتغيّرة.

# Create the namespace and make it the default for this context kubectl create notes kubectl config set-context --current --=notes # Store the database password as a Secret kubectl create secret notes-db --from-literal=POSTGRES_PASSWORD=dev-only-change-me # Read it back (and see that it isn't encrypted) kubectl get secret notes-db -o jsonpath='{.data.POSTGRES_PASSWORD}' | base64 # Pick up changed environment variables in running Pods kubectl rollout deployment/notes-api