پرش به محتویات

آموزش کوبرنتیز؛ شب هشتم: ConfigMapها

امشب پیکربندی غیرمحرمانه را از Image جدا می‌کنیم، آن را هم به‌شکل متغیر محیطی و هم فایل Mountشده مصرف می‌کنیم و تفاوت رفتار این دو روش هنگام به‌روزرسانی را می‌بینیم.

مدل ذهنی ConfigMap

ConfigMap داده‌های کلید/مقدار را در API کوبرنتیز نگه می‌دارد. Pod می‌تواند یک کلید مشخص را مصرف کند، کلیدها را به متغیر محیطی تبدیل کند یا آن‌ها را به‌شکل فایل نمایش دهد. ConfigMap فقط برای پیکربندی غیرمحرمانه مناسب است.

ConfigMap key ── valueFrom ──> environment variable (captured at process start)
ConfigMap key ── volume ─────> file (eventually refreshed by kubelet)

وقتی ConfigMap تغییر می‌کند، متغیرهای محیطی داخل Process موجود تغییر نمی‌کنند. فایل‌های Projectشده معمولاً با کمی تأخیر به‌روزرسانی می‌شوند، نه بلافاصله. Mount کردن با subPath نیز به‌روزرسانی خودکار ConfigMap را دریافت نمی‌کند.

Manifest برنامه

فایل app.yaml یک ConfigMap را از دو مسیر در اختیار NGINX قرار می‌دهد:

apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
  namespace: course-night-8
data:
  COURSE_NIGHT: "8"
  index.html: |
    <!doctype html>
    <html><body><h1>Configuration version 1</h1></body></html>
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: course-night-8
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27-alpine
          env:
            - name: COURSE_NIGHT
              valueFrom:
                configMapKeyRef:
                  name: web-config
                  key: COURSE_NIGHT
          ports:
            - name: http
              containerPort: 80
          volumeMounts:
            - name: content
              mountPath: /usr/share/nginx/html
              readOnly: true
      volumes:
        - name: content
          configMap:
            name: web-config
            items:
              - key: index.html
                path: index.html
---
apiVersion: v1
kind: Service
metadata:
  name: web
  namespace: course-night-8
spec:
  selector:
    app: web
  ports:
    - name: http
      port: 80
      targetPort: http

استقرار و بررسی

kubectl create namespace course-night-8
kubectl apply -f app.yaml
kubectl rollout status deployment/web --namespace course-night-8
kubectl get configmap web-config --namespace course-night-8 -o yaml

kubectl exec --namespace course-night-8 deployment/web -- \
  sh -c 'printf "COURSE_NIGHT=%s\n" "$COURSE_NIGHT"; cat /usr/share/nginx/html/index.html'

kubectl run client --namespace course-night-8 --rm -it \
  --restart=Never --image=curlimages/curl -- curl --fail http://web

Volume مربوط به ConfigMap محتوای Image را در دایرکتوری Mountشده جایگزین می‌کند. اگر فایل‌های اصلی دایرکتوری اهمیت دارند، یک فایل مشخص را Mount کنید یا تمام فایل‌های لازم را در ConfigMap قرار دهید.

روش‌های مختلف ساخت ConfigMap

فرمان‌های زیر شیوه‌های ساخت را بدون Apply کردن Object اضافی نشان می‌دهند:

kubectl create configmap literal-demo --namespace course-night-8 \
  --from-literal=color=blue --dry-run=client -o yaml

kubectl create configmap file-demo --namespace course-night-8 \
  --from-file=index.html --dry-run=client -o yaml

برای پیکربندی ماندگار، Manifest اعلانی انتخاب بهتری است؛ زیرا بازبینی‌پذیر و قابل بازتولید است.

آزمایش رفتار به‌روزرسانی

متن index.html در app.yaml را به Configuration version 2 تغییر دهید:

kubectl diff -f app.yaml
kubectl apply -f app.yaml
kubectl get configmap web-config --namespace course-night-8 \
  -o jsonpath='{.data.index\.html}{"\n"}'

تا دو دقیقه فایل Mountشده را بررسی کنید:

kubectl exec --namespace course-night-8 deployment/web -- \
  sh -c 'while ! grep -q "version 2" /usr/share/nginx/html/index.html; do date; sleep 5; done; cat /usr/share/nginx/html/index.html'

اکنون مقدار COURSE_NIGHT را در Manifest به "88" تغییر و Apply کنید. ConfigMap تغییر می‌کند، اما متغیر محیطی Podهای موجود همچنان 8 باقی می‌ماند:

kubectl apply -f app.yaml
kubectl exec --namespace course-night-8 deployment/web -- \
  printenv COURSE_NIGHT
kubectl rollout restart deployment/web --namespace course-night-8
kubectl rollout status deployment/web --namespace course-night-8
kubectl exec --namespace course-night-8 deployment/web -- \
  printenv COURSE_NIGHT

Rollout، Processهای تازه‌ای می‌سازد که مقدار جدید را می‌خوانند. در محیط تولید معمولاً از Checksum پیکربندی در Annotation مربوط به Pod Template استفاده می‌شود تا این Restart خودکار انجام شود.

تمرین خرابی: کلید گم‌شده

موقتاً مقدار configMapKeyRef.key را از COURSE_NIGHT به MISSING تغییر دهید، Apply کنید و Pod جدید را بررسی کنید:

kubectl apply -f app.yaml
kubectl get pods --namespace course-night-8
kubectl describe pod <new-pod-name> --namespace course-night-8
kubectl get events --namespace course-night-8 \
  --sort-by=.metadata.creationTimestamp

Container به‌دلیل نبودن یک کلید اجباری شروع نمی‌شود. نام کلید را تعمیر، دوباره Apply و تا پایان Rollout صبر کنید.

چک‌لیست پایان

  • یک ConfigMap را هم از طریق متغیر محیطی و هم فایل مصرف کرده‌ام.
  • رفتار به‌روزرسانی هر دو روش را توضیح می‌دهم.
  • خرابی ناشی از کلید گم‌شده را تشخیص داده و تعمیر کرده‌ام.
  • توضیح می‌دهم چرا Password و Token نباید داخل ConfigMap باشند.
kubectl delete namespace course-night-8