حالت تاریک
فصل ۱۳ — ConfigMapها و Secretها
بهترین practice این است که container imageها تا حد ممکن reusable باشند. همان image باید برای development، staging و production قابل استفاده باشد. حتی بهتر است image بهاندازه کافی general-purpose باشد که در applicationها و serviceهای مختلف استفاده شود. testing و versioning اگر image برای هر environment جدید recreate شود پرریسکتر و پیچیدهتر میشود. پس چطور استفاده از آن image را در runtime تخصصی کنیم؟
اینجا ConfigMapها و Secretها وارد میشوند. ConfigMapها برای فراهم کردن اطلاعات پیکربندی به workloadها استفاده میشوند. این میتواند اطلاعات fine-grained (یک string کوتاه) یا مقدار composite بهصورت فایل باشد. Secretها شبیه ConfigMapها هستند اما روی در دسترس قرار دادن اطلاعات حساس به workload تمرکز دارند. برای credentialها یا certificateهای TLS استفاده میشوند.
ConfigMapها
یک ConfigMap را میتوان بهعنوان شیء Kubernetes که یک file-system کوچک تعریف میکند در نظر گرفت. راه دیگر، مجموعهای از variableهاست که هنگام تعریف environment یا command line برای containerهایتان استفاده میشوند. نکته کلیدی این است که ConfigMap درست قبل از run شدن با Pod ترکیب میشود. یعنی container image و خود تعریف Pod را میتوان در many app با فقط تغییر ConfigMap استفادهشده reuse کرد.
ساخت ConfigMapها
بیایید مستقیم یک ConfigMap بسازیم. مثل many object در Kubernetes، میتوانید بهصورت immediate و imperative یا از manifest روی disk بسازید. با روش imperative شروع میکنیم.
فرض کنید فایلی روی disk داریم (my-config.txt) که میخواهیم به Pod مورد نظر در دسترس باشد، همانطور که در مثال ۱۳-۱ نشان داده شده.
مثال ۱۳-۱. my-config.txt
# This is a sample config file that I might use to configure an application
parameter1 = value1
parameter2 = value2بعد ConfigMap با آن فایل میسازیم. چند جفت key/value ساده هم اضافه میکنیم. در خط فرمان به آنها literal value گفته میشود:
$ kubectl create configmap my-config \
--from-file=my-config.txt \
--from-literal=extra-param=extra-value \
--from-literal=another-param=another-valueYAML معادل شیء ConfigMap که ساختیم:
$ kubectl get configmaps my-config -o yaml
apiVersion: v1
data:
another-param: another-value
extra-param: extra-value
my-config.txt: |
# This is a sample config file that I might use to configure an application
parameter1 = value1
parameter2 = value2
kind: ConfigMap
metadata:
creationTimestamp: ...
name: my-config
namespace: default
resourceVersion: "13556"
selfLink: /api/v1/namespaces/default/configmaps/my-config
uid: 3641c553-f7de-11e6-98c9-06135271a273همانطور که میبینید، ConfigMap واقعاً فقط چند جفت key/value در یک object است. بخش جالب وقتی شروع میکنید ConfigMap استفاده کنید.
استفاده از ConfigMap
سه راه اصلی برای استفاده از ConfigMap وجود دارد:
Filesystem
میتوانید ConfigMap را در Pod mount کنید. برای هر entry بر اساس key name یک فایل ساخته میشود. محتوای آن فایل روی value تنظیم میشود.
Environment variable
ConfigMap میتواند برای تنظیم dynamic مقدار environment variable استفاده شود.
Command-line argument
Kubernetes از ساخت dynamic command line برای container بر اساس valueهای ConfigMap پشتیبانی میکند.
manifest برای kuard بسازیم که همه اینها را با هم میآورد، همانطور که در مثال ۱۳-۲ نشان داده شده.
مثال ۱۳-۲. kuard-config.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: kuard-config
spec:
containers:
- name: test-container
image: gcr.io/kuar-demo/kuard-amd64:blue
imagePullPolicy: Always
command:
- "/kuard"
- "$(EXTRA_PARAM)"
env:
- name: ANOTHER_PARAM
valueFrom:
configMapKeyRef:
name: my-config
key: another-param
- name: EXTRA_PARAM
valueFrom:
configMapKeyRef:
name: my-config
key: extra-param
volumeMounts:
- name: config-volume
mountPath: /config
volumes:
- name: config-volume
configMap:
name: my-config
restartPolicy: Neverبرای روش filesystem، volume جدید داخل Pod میسازیم و نام config-volume میدهیم. این volume را ConfigMap volume تعریف میکنیم و به ConfigMap mount اشاره میکنیم. باید مشخص کنیم کجا در container kuard mount شود با volumeMount. اینجا در /config mount میکنیم.
Environment variableها با member خاص valueFrom مشخص میشوند. این به ConfigMap و data key مورد استفاده در آن ConfigMap reference میدهد.
Command-line argumentها روی environment variableها بنا میشوند. Kubernetes substitution درست را با syntax خاص $(<env-var-name>) انجام میدهد.
این Pod را run کنید و port forward بزنید تا ببینید application دنیا را چطور میبیند:
$ kubectl apply -f kuard-config.yaml
$ kubectl port-forward kuard-config 8080مرورگر را به http://localhost:8080 ببرید. میتوانیم ببینیم valueهای پیکربندی را در هر سه روش inject کردهایم.
تب «Server Env» در سمت چپ را کلیک کنید. command line که application با آن launch شده و environment آن را نشان میدهد (شکل ۱۳-۱).
شکل ۱۳-۱. kuard که environment خود را نشان میدهد
اینجا میبینیم دو environment variable (ANOTHER_PARAM و EXTRA_PARAM) اضافه کردهایم که valueهایشان از ConfigMap تنظیم میشود. علاوه بر این، argumentی به command line kuard بر اساس value EXTRA_PARAM اضافه کردهایم.
بعد تب «File system browser» را کلیک کنید (شکل ۱۳-۲). filesystem را همانطور که application میبیند explore میکند. entry بهنام /config باید ببینید. این volume بر اساس ConfigMap ما ساخته شده. اگر داخل آن بروید، برای هر entry از ConfigMap یک فایل ساخته شده. فایلهای hidden (با .. در ابتدا) هم میبینید که برای swap تمیز valueهای جدید وقتی ConfigMap بهروز میشود استفاده میشوند.
شکل ۱۳-۲. دایرکتوری
/configاز دید kuard
Secretها
ConfigMapها برای بیشتر دادههای پیکربندی عالی هستند، اما دادهای هست که extra-sensitive است. شامل passwordها، security tokenها یا انواع دیگر private key. جمعاً این نوع داده را «secret» مینامیم. Kubernetes پشتیبانی native برای ذخیره و handle کردن این داده با دقت دارد.
Secretها به container imageها اجازه میدهند بدون bundle کردن داده حساس ساخته شوند. این containerها را portable در environmentهای مختلف نگه میدارد. Secretها از طریق declaration صریح در Pod manifestها و Kubernetes API به Podها expose میشوند. به این ترتیب، Kubernetes secrets API مکانیزم application-centric برای expose کردن اطلاعات پیکربندی حساس به applicationها فراهم میکند که audit آسان است و از primitiveهای isolation native OS بهره میبرد.
بهطور پیشفرض، Kubernetes Secretها بهصورت plain text در etcd storage برای cluster ذخیره میشوند. بسته به نیازهایتان، ممکن است امنیت کافی نباشد. بهویژه هر کسی که حق cluster administration در cluster دارد میتواند همه Secretها را بخواند. در نسخههای اخیر Kubernetes، پشتیبانی encrypt کردن Secretها با key تأمینشده توسط کاربر اضافه شده، معمولاً integrated در cloud key store. علاوه بر این، بیشتر cloud key storeها integration با Kubernetes flexible volume دارند تا بتوانید کاملاً از Kubernetes Secret صرفنظر کنید و فقط به key store ارائهدهنده cloud تکیه کنید. همه این گزینهها ابزار کافی برای ساخت security profile متناسب با نیازتان میدهند.
بقیه این بخش نحوه ساخت و مدیریت Kubernetes Secretها و best practice برای expose کردن Secretها به Podهایی که به آنها نیاز دارند را بررسی میکند.
ساخت Secretها
Secretها با Kubernetes API یا ابزار خط فرمان kubectl ساخته میشوند. Secretها یک یا چند data element را بهصورت مجموعهای از جفت key/value نگه میدارند.
در این بخش Secretی میسازیم تا TLS key و certificate برای application kuard را ذخیره کند که الزامات storage قبلی را برآورده میکند.
container image kuard TLS certificate یا key bundle نمیکند. این به container kuard اجازه میدهد portable در environmentهای مختلف بماند و از Docker repositoryهای public توزیع شود.
اولین قدم ساخت Secret، گرفتن raw dataای است که میخواهیم ذخیره کنیم. TLS key و certificate برای application kuard با اجرای دستورات زیر دانلود میشوند:
$ curl -o kuard.crt https://storage.googleapis.com/kuar-demo/kuard.crt
$ curl -o kuard.key https://storage.googleapis.com/kuar-demo/kuard.keyاین certificateها با دنیا به اشتراک گذاشته شدهاند و امنیت واقعی ندارند. لطفاً جز بهعنوان ابزار یادگیری در این مثالها استفاده نکنید.
با فایلهای kuard.crt و kuard.key ذخیرهشده local، آماده ساخت Secret هستیم. Secret بهنام kuard-tls با دستور create secret بسازید:
$ kubectl create secret generic kuard-tls \
--from-file=kuard.crt \
--from-file=kuard.keySecret kuard-tls با دو data element ساخته شده. برای جزئیات:
$ kubectl describe secrets kuard-tls
Name: kuard-tls
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
kuard.crt: 1050 bytes
kuard.key: 1679 bytesبا Secret kuard-tls آماده، میتوانیم از Pod با secrets volume consume کنیم.
مصرف Secretها
Secretها میتوانند با Kubernetes REST API توسط applicationهایی که مستقیماً آن API را call میکنند consume شوند. اما هدف ما portable نگه داشتن applicationهاست. نه فقط در Kubernetes خوب run شوند، بلکه بدون تغییر روی platformهای دیگر هم run شوند.
بهجای دسترسی به Secret از طریق API server، میتوانیم secrets volume استفاده کنیم.
Secrets volume
داده Secret میتواند با volume type secrets به Podها expose شود. Secrets volumeها توسط kubelet مدیریت میشوند و در زمان Pod creation ساخته میشوند. Secretها روی tmpfs volume (یعنی RAM disk) ذخیره میشوند و روی node به disk نوشته نمیشوند.
هر data element از Secret در فایل جداگانهای زیر mount point هدف مشخصشده در volume mount ذخیره میشود. Secret kuard-tls دو data element دارد: kuard.crt و kuard.key. mount کردن secrets volume kuard-tls به /tls این فایلها را میسازد:
/tls/kuard.crt
/tls/kuard.keyPod manifest در مثال ۱۳-۳ نشان میدهد چطور secrets volume declare کنیم که Secret kuard-tls را به container kuard زیر /tls expose میکند.
مثال ۱۳-۳. kuard-secret.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: kuard-tls
spec:
containers:
- name: kuard-tls
image: gcr.io/kuar-demo/kuard-amd64:blue
imagePullPolicy: Always
volumeMounts:
- name: tls-certs
mountPath: "/tls"
readOnly: true
volumes:
- name: tls-certs
secret:
secretName: kuard-tlsPod kuard-tls را با kubectl بسازید و log output از Pod running را observe کنید:
$ kubectl apply -f kuard-secret.yamlبا اجرای زیر به Pod وصل شوید:
$ kubectl port-forward kuard-tls 8443:8443مرورگر را به https://localhost:8443 ببرید. باید warningهای invalid certificate ببینید چون self-signed certificate برای kuard.example.com است. اگر از warning عبور کنید، باید سرور kuard hosted via HTTPS را ببینید. از تب «File system browser» certificateها را روی disk پیدا کنید.
Private Docker Registryها
use case خاص Secretها ذخیره access credential برای private Docker registry است. Kubernetes از image ذخیرهشده روی private registry پشتیبانی میکند، اما دسترسی به آن imageها credential میخواهد. image private میتواند در یک یا چند private registry ذخیره شود. این challenge مدیریت credential برای هر private registry روی هر node ممکن در cluster میسازد.
Image pull secret از secrets API برای automate کردن توزیع credential private registry استفاده میکند. Image pull secret مثل Secret معمولی ذخیره میشود اما از طریق فیلد Pod specification spec.imagePullSecrets consume میشود.
از create secret docker-registry برای ساخت این نوع خاص Secret استفاده کنید:
$ kubectl create secret docker-registry my-image-pull-secret \
--docker-username=<username> \
--docker-password=<password> \
--docker-email=<email-address>دسترسی به repository private را با reference کردن image pull secret در فایل Pod manifest فعال کنید، همانطور که در مثال ۱۳-۴ نشان داده شده.
مثال ۱۳-۴. kuard-secret-ips.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: kuard-tls
spec:
containers:
- name: kuard-tls
image: gcr.io/kuar-demo/kuard-amd64:blue
imagePullPolicy: Always
volumeMounts:
- name: tls-certs
mountPath: "/tls"
readOnly: true
imagePullSecrets:
- name: my-image-pull-secret
volumes:
- name: tls-certs
secret:
secretName: kuard-tlsاگر مکرراً از همان registry pull میکنید، میتوانید Secretها را به default service account مرتبط با هر Pod اضافه کنید تا در هر Pod که میسازید مجبور نباشید Secretها را مشخص کنید.
محدودیتهای نامگذاری
key name برای data item داخل Secret یا ConfigMap طوری تعریف شده که به valid environment variable name map شود. میتوانند با dot و بعد letter یا number شروع شوند. کاراکترهای بعدی شامل dot، dash و underscore هستند. dot نمیتواند تکرار شود و dot و underscore یا dash نمیتوانند adjacent باشند.
بهطور formal، یعنی باید با regular expression زیر conform باشند:
^[.[?[a-zAZ0-9[([.[?[a-zA-Z0-9[+[-_a-zA-Z0-9[?)*$چند مثال valid و invalid برای ConfigMap و Secret در جدول ۱۳-۱ آمده.
| نام key معتبر | نام key نامعتبر |
|---|---|
| .auth_token | Token..properties |
| Key.pem | auth file.json |
| config_file | _password.txt |
هنگام انتخاب key name، در نظر بگیرید این keyها میتوانند از طریق volume mount به Pod expose شوند. نامی انتخاب کنید که در command line یا config file معنا داشته باشد. ذخیره TLS key بهنام
key.pemازtls-keyهنگام configure کردن application برای دسترسی به Secret واضحتر است.
valueهای data ConfigMap متن UTF-8 ساده مستقیماً در manifest مشخص میشوند. از Kubernetes 1.6، ConfigMapها نمیتوانند binary data ذخیره کنند.
valueهای data Secret داده arbitrary با encoding base64 نگه میدارد. استفاده از base64 encoding ذخیره binary data را ممکن میکند. اما مدیریت Secret ذخیرهشده در فایل YAML را سختتر میکند چون value base64-encoded باید در YAML باشد. توجه کنید حداکثر اندازه ConfigMap یا Secret ۱ MB است.
مدیریت ConfigMapها و Secretها
Secretها و ConfigMapها از طریق Kubernetes API مدیریت میشوند. دستورات معمول create، delete، get و describe برای manipulate کردن این objectها کار میکنند.
Listing
با kubectl get secrets همه Secretها در namespace فعلی را list کنید:
$ kubectl get secrets
NAME TYPE DATA AGE
default-token-f5jq2 kubernetes.io/service-account-token 3 1h
kuard-tls Opaque 2 20mبههمین شکل همه ConfigMapها در namespace:
$ kubectl get configmaps
NAME DATA AGE
my-config 3 1mkubectl describe برای جزئیات بیشتر روی یک object:
$ kubectl describe configmap my-config
Name: my-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
another-param: 13 bytes
extra-param: 11 bytes
my-config.txt: 116 bytesدر نهایت raw data (شامل valueها در Secret!) را با چیزی مثل kubectl get configmap my-config -o yaml یا kubectl get secret kuard-tls -o yaml میبینید.
Creating
سادهترین راه ساخت Secret یا ConfigMap از طریق kubectl create secret generic یا kubectl create configmap است. راههای مختلفی برای مشخص کردن data itemهایی که وارد Secret یا ConfigMap میشوند وجود دارد. میتوان در یک دستور ترکیب کرد:
--from-file=<filename>
از فایل load میکند؛ secret data key همان filename است.
--from-file=<key>=<filename>
از فایل load میکند؛ secret data key صریحاً مشخص شده.
--from-file=<directory>
همه فایلهای directory مشخصشده را load میکند؛ filename باید key name قابل قبول باشد.
--from-literal=<key>=<value>
جفت key/value مشخصشده را مستقیماً استفاده میکند.
Updating
میتوانید ConfigMap یا Secret را بهروز کنید و در برنامههای running منعکس شود. اگر application برای re-read کردن valueهای پیکربندی configure شده باشد restart لازم نیست. این feature نادر است اما ممکن است در applicationهای خودتان بگذارید.
سه راه برای بهروزرسانی ConfigMap یا Secret:
Update from file
اگر manifest برای ConfigMap یا Secret دارید، مستقیماً edit کنید و نسخه جدید را با kubectl replace -f <filename> push کنید. اگر قبلاً resource را با kubectl apply ساختهاید میتوانید kubectl apply -f <filename> هم استفاده کنید.
بهخاطر نحوه encode شدن datafile در این objectها، بهروزرسانی پیکربندی کمی cumbersome است چون provisionی در kubectl برای load از فایل external نیست. data باید مستقیماً در YAML manifest ذخیره شود.
use case رایج وقتی است ConfigMap بهعنوان بخشی از directory یا list resource تعریف شده و همه با هم create و update میشوند. اغلب این manifestها در source control check in میشوند.
بهطور کلی check in کردن فایل YAML Secret به source control ایده بد است. push کردن این فایلها جایی public و leak کردن Secretها خیلی آسان است.
Recreate and update
اگر inputهای ConfigMap یا Secret را بهصورت فایل جداگانه روی disk نگه میدارید (بهجای embed مستقیم در YAML)، میتوانید kubectl را برای recreate کردن manifest و سپس update کردن object استفاده کنید.
چیزی شبیه این:
$ kubectl create secret generic kuard-tls \
--from-file=kuard.crt --from-file=kuard.key \
--dry-run -o yaml | kubectl replace -f -این command line اول Secret جدید با همان نام Secret موجود میسازد. اگر همینجا stop کنیم، Kubernetes API server error برمیگرداند که Secret از قبل وجود دارد. بهجای آن به kubectl میگوییم data را واقعاً به server نفرستد بلکه YAMLای که میفرستاد به stdout dump کند. سپس به kubectl replace pipe میکنیم و -f - میگوییم از stdin بخواند. اینطور Secret را از فایل روی disk بدون base64-encode دستی data بهروز میکنیم.
Edit current version
راه نهایی بهروزرسانی ConfigMap استفاده از kubectl edit برای باز کردن نسخه ConfigMap در editor تا tweak کنید (با Secret هم میتوانید، اما base64 encoding valueها را خودتان manage میکنید):
$ kubectl edit configmap my-configتعریف ConfigMap را در editor میبینید. تغییرات را save و close کنید. نسخه جدید object به Kubernetes API server push میشود.
Live updates
وقتی ConfigMap یا Secret با API بهروز شد، خودکار به همه volumeهایی که از آن ConfigMap یا Secret استفاده میکنند push میشود. ممکن است چند ثانیه طول بکشد، اما file listing و محتوای فایلها از دید kuard با valueهای جدید بهروز میشوند. با این feature live update میتوانید پیکربندی application را بدون restart بهروز کنید.
فعلاً راه built-in برای signal دادن به application وقتی نسخه جدید ConfigMap deploy میشود نیست. به application (یا helper script) بستگی دارد config fileها را برای تغییر ببیند و reload کند.
file browser در kuard (از طریق kubectl port-forward) راه عالی برای بازی interactive با بهروزرسانی dynamic Secret و ConfigMap است.
خلاصه
ConfigMapها و Secretها راه عالی برای پیکربندی dynamic در application شما هستند. به شما اجازه میدهند container image (و تعریف Pod) را یکبار بسازید و در contextهای مختلف reuse کنید. این شامل استفاده از همان image هنگام حرکت از dev به staging به production است. همچنین استفاده از یک image در چند team و service. جدا کردن پیکربندی از application code applicationها را reliableتر و reusableتر میکند.