ملاحظة : هذه ليست مقالة إرشادية كاملة ، بل هي تذكير / تلميح لأولئك الذين يستخدمون ConfigMap بالفعل في Kubernetes أو يقومون فقط بإعداد تطبيقهم للعمل فيه.
الخلفية: من rsync إلى ... Kubernetes
ماذا حدث من قبل؟ في عصر "الإدارة الكلاسيكية" ، في أبسط إصدار ، تم وضع ملف التكوين بجوار التطبيقات مباشرة (أو في المستودع ، إذا أردت). الأمر بسيط: نحن نجعل التسليم الأولي (CD) لرمزنا مع التكوين. حتى تطبيق rsync الشرطي يمكن أن يسمى أساسيات القرص المضغوط.عندما نمت البنية التحتية ، كانت هناك حاجة إلى تكوينات مختلفة لبيئات مختلفة (dev / stage / production). تم تدريب التطبيق على فهم التكوين الذي سيتم استخدامه ، وتمريرها كوسيطة للبدء أو متغيرات البيئة المعينة في البيئة. حتى المزيد من القرص المضغوط معقد مع ظهور مثل هذا الشيف / العرائس / Ansible المفيد. تظهر الأدوار في الخوادم ، ويتوقف وصف البيئات في أماكن مختلفة - نأتي إلى IaC (البنية التحتية كرمز).ما أعقب ذلك؟ إذا كان من الممكن أن نرى مزايا Kubernetes الحاسمة لنفسها وحتى تتصالح مع الحاجة إلى تعديل التطبيقات للعمل في هذه البيئة ، حدث الترحيل. في الطريق ، توقعت الكثير من الفروق الدقيقة والاختلافات في بناء الهندسة المعمارية ، ولكن عندما تمكنت من التعامل مع الجزء الرئيسي ، حصلت على التطبيق الذي طال انتظاره يعمل في K8s.مرة واحدة هنا ، لا يزال بإمكاننا استخدام التكوينات المعدة في المستودع بجوار التطبيق أو تمرير ENV إلى الحاوية. ومع ذلك ، بالإضافة إلى هذه الأساليب ، تتوفر ConfigMaps أيضًا . يتيح لك K8s البدائي استخدام قوالب Go في التكوينات ، أي تقديمها مثل صفحات HTML وإعادة تحميل التطبيق عند تغيير التكوين دون إعادة التشغيل. مع ConfigMaps ، لم تعد هناك حاجة للحفاظ على 3+ تكوينات لبيئات مختلفة وتتبع مدى ملاءمة كل منها.يمكن العثور على مقدمة عامة عن ConfigMaps ، على سبيل المثال ، هنا . وسأركز في هذه المقالة على بعض ميزات العمل معهم.تكوينات بسيطة
كيف تبدو التكوينات في Kubernetes؟ ما الذي حصلوا عليه من قوالب الذهاب؟ على سبيل المثال ، فيما يلي ملف ConfigMap عادي لتطبيق تم نشره من مخطط Helm:apiVersion: v1
kind: ConfigMap
metadata:
name: app
data:
config.json: |
{
"welcome": {{ pluck .Values.global.env .Values.welcome | quote }},
"name": {{ pluck .Values.global.env .Values.name | quote }}
}
هنا القيم استبداله في .Values.welcome
و .Values.name
ستتخذ من الملف values.yaml
. لماذا بالضبط values.yaml
؟ كيف يعمل محرك قالب Go؟ لقد تحدثنا بالفعل عن هذه التفاصيل بمزيد من التفاصيل هنا . تساعدالمكالمة pluck
على تحديد الخط الضروري من الخريطة:$ cat .helm/values.yaml
welcome:
production: "Hello"
test: "Hey"
name:
production: "Bob"
test: "Mike"
علاوة على ذلك ، يمكنك أخذ كل من خطوط محددة وأجزاء كاملة من التكوين.على سبيل المثال ، قد يكون ConfigMap كما يلي:data:
config.json: |
{{ pluck .Values.global.env .Values.data | first | toJson | indent 4 }}
... وفي values.yaml
- المحتويات التالية:data:
production:
welcome: "Hello"
name: "Bob"
المعني هنا global.env
هو اسم البيئة. باستبدال هذه القيمة أثناء النشر ، يمكنك تقديم ConfigMaps بمحتوى مختلف. first
مطلوب هنا بسبب pluck
تقوم بإرجاع قائمة ، يحتوي العنصر الأول منها على القيمة المطلوبة.عندما يكون هناك الكثير من التكوينات
يمكن أن يحتوي ConfigMap واحد على العديد من ملفات التكوين:data:
config.json: |
{
"welcome": {{ pluck .Values.global.env .Values.welcome | first | quote }},
"name": {{ pluck .Values.global.env .Values.name | first | quote }}
}
database.yml: |
host: 127.0.0.1
db: app
user: app
password: app
يمكنك حتى تثبيت كل تكوين على حدة: volumeMounts:
- name: app-conf
mountPath: /app/configfiles/config.json
subPath: config.json
- name: app-conf
mountPath: /app/configfiles/database.yml
subPath: database.yml
... أو احصل على جميع التكوينات مرة واحدة مع الدليل: volumeMounts:
- name: app-conf
mountPath: /app/configfiles
إذا قمت بتغيير وصف مورد النشر أثناء النشر ، فسيقوم Kubernetes بإنشاء ReplicaSet جديدة ، وتقليل القديم إلى 0 وزيادة الجديد إلى العدد المحدد من النسخ المتماثلة. (هذا صحيح بالنسبة لاستراتيجية النشر RollingUpdate
.)ستؤدي هذه الإجراءات إلى إعادة إنشاء لوحة مع وصف جديد. على سبيل المثال: كانت هناك صورة image:my-registry.example.com:v1
لكنها أصبحت - image:my-registry.example.com:v2
. ولا يهم على الإطلاق ما غيرناه تمامًا في وصف النشر لدينا: الشيء الرئيسي هو أن هذا تسبب في إعادة إنشاء النسخة المتماثلة (ونتيجة لذلك ، إعادة إنشاء البودرة). في هذه الحالة ، يتم تحميل الإصدار الجديد من ملف التكوين في الإصدار الجديد من التطبيق تلقائيًا ولن تكون هناك مشكلة.استجابة تغيير ConfigMap
في حالة حدوث تغييرات في ConfigMap ، يمكن متابعة أربعة سيناريوهات للأحداث. اعتبرهم:- : ConfigMap, subPath.
: . - : ConfigMap, pod.
: pod . - : ConfigMap Deployment -.
: , ConfigMap’, Deployment, pod , — . - : ConfigMap, .
: pod’ / pod’.
سنحلل بمزيد من التفصيل.السيناريو 1
نحن تصحيح تصحيح ConfigMap فقط ؟ لن يتم إعادة تشغيل التطبيق. في حالة التثبيت ، subPath
لن تكون هناك تغييرات حتى يتم إعادة تشغيل اللوحة يدويًا.كل شيء بسيط: تقوم Kubernetes بتثبيت ConfigMap الخاص بنا في جراب من إصدار محدد من المورد. نظرًا لأنه تم تثبيته مع subPath
، لم يعد يتم توفير "تأثير" إضافي على التكوين.السيناريو 2
لا يمكنك تحديث الملف دون إعادة إنشاء جراب؟ حسنًا ، لدينا 6 نسخ متماثلة في النشر ، حتى نتمكن من التناوب في القيام بكل شيء يدويًا delete pod
. ثم عند إنشاء منصات جديدة ، سوف "يلتقطون" الإصدار الجديد من ConfigMap.السيناريو 3
هل سئمت من إجراء مثل هذه العمليات يدويًا؟ يتم وصف حل هذه المشكلة في نصائح وحيل هيلم :kind: Deployment
spec:
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
[...]
وبالتالي ، تتم كتابة spec.template
تجزئة تكوين التعليقات التوضيحية المقدمة في قالب pod ( ).التعليقات التوضيحية هي حقول قيم رئيسية عشوائية يمكنك تخزين قيمك فيها. إذا قمت بتسجيلها في قالب spec.template
pod المستقبلي ، ستندرج هذه الحقول في ReplicaSet و pod نفسها. ستلاحظ Kubernetes أن قالب pod قد تغير (منذ أن تم تغيير تهيئة sha256) وسيبدأ RollingUpdate
، حيث لا يتغير شيء باستثناء هذا التعليق التوضيحي.ونتيجة لذلك ، نحفظ نفس الإصدار من تطبيق ووصف النشر ، ونبدأ فقط في إعادة إنشاء الجهاز بواسطة الجهاز - على غرار الطريقة التي سنقوم بها يدويًا kubectl delete
، ولكن بالفعل "بشكل صحيح": تلقائيًا وبواسطة RollingUpdate
.السيناريو 4
ربما يكون التطبيق يعرف بالفعل كيفية مراقبة التغييرات في التكوين وإعادة التحميل تلقائيًا؟ هنا تكمن ميزة مهمة في ConfigMaps ...في Kubernetes ، إذا تم تحميل التكوين subPath
، فلن يتم تحديثه حتى يتم إعادة تشغيل pod (راجع السيناريوهات الثلاثة الأولى التي نوقشت أعلاه) . ولكن إذا قمت بتثبيت ConfigMap كدليل ، بدون subPath
، ثم داخل الحاوية سيكون هناك دليل مع تكوين محدث دون إعادة تشغيل pod.هناك ميزات أخرى مفيدة للتذكر:- يتم تحديث ملف التكوين المحدث داخل الحاوية مع بعض التأخير. هذا يرجع إلى حقيقة أن الملف لم يتم تحميله بالضبط ، ولكن كائن Kubernetes.
- الملف الموجود في الداخل هو ارتباط رمزي. مثال مع
subPath
:
$ kubectl -n production exec go-conf-example-6b4cb86569-22vqv -- ls -lha /app/configfiles
total 20K
drwxr-xr-x 1 root root 4.0K Mar 3 19:34 .
drwxr-xr-x 1 app app 4.0K Mar 3 19:34 ..
-rw-r--r-- 1 root root 42 Mar 3 19:34 config.json
-rw-r--r-- 1 root root 47 Mar 3 19:34 database.yml
وماذا سيحدث بدون subPath
تركيب الدليل؟
$ kubectl -n production exec go-conf-example-67c768c6fc-ccpwl -- ls -lha /app/configfiles
total 12K
drwxrwxrwx 3 root root 4.0K Mar 3 19:40 .
drwxr-xr-x 1 app app 4.0K Mar 3 19:34 ..
drwxr-xr-x 2 root root 4.0K Mar 3 19:40 ..2020_03_03_16_40_36.675612011
lrwxrwxrwx 1 root root 31 Mar 3 19:40 ..data -> ..2020_03_03_16_40_36.675612011
lrwxrwxrwx 1 root root 18 Mar 3 19:40 config.json -> ..data/config.json
lrwxrwxrwx 1 root root 19 Mar 3 19:40 database.yml -> ..data/database.yml
قم بتحديث التكوين (عن طريق kubectl edit
النشر أو ) ، وانتظر دقيقتين (وقت التخزين المؤقت apiserver) - وفويلا:
$ kubectl -n production exec go-conf-example-67c768c6fc-ccpwl -- ls -lha --color /app/configfiles
total 12K
drwxrwxrwx 3 root root 4.0K Mar 3 19:44 .
drwxr-xr-x 1 app app 4.0K Mar 3 19:34 ..
drwxr-xr-x 2 root root 4.0K Mar 3 19:44 ..2020_03_03_16_44_38.763148336
lrwxrwxrwx 1 root root 31 Mar 3 19:44 ..data -> ..2020_03_03_16_44_38.763148336
lrwxrwxrwx 1 root root 18 Mar 3 19:40 config.json -> ..data/config.json
lrwxrwxrwx 1 root root 19 Mar 3 19:40 database.yml -> ..data/database.yml
لاحظ الطابع الزمني الذي تم تغييره في الدليل الذي تم إنشاؤه بواسطة Kubernetes.
تتبع التغيير
وأخيرًا - مثال بسيط على كيفية مراقبة التغييرات في التكوين.سوف نستخدم تطبيق Go-applicationpackage main
import (
"encoding/json"
"fmt"
"log"
"os"
"time"
"github.com/fsnotify/fsnotify"
)
type Config struct {
Welcome string `json:"welcome"`
Name string `json:"name"`
}
var (
globalConfig *Config
)
func LoadConfig(path string) (*Config, error) {
configFile, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("Unable to read configuration file %s", path)
}
config := new(Config)
decoder := json.NewDecoder(configFile)
err = decoder.Decode(&config)
if err != nil {
return nil, fmt.Errorf("Unable to parse configuration file %s", path)
}
return config, nil
}
func ConfigWatcher() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
done := make(chan bool)
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
log.Println("event:", event)
if event.Op&fsnotify.Write == fsnotify.Write {
log.Println("modified file:", event.Name)
}
globalConfig, _ = LoadConfig("./configfiles/config.json")
log.Println("config:", globalConfig)
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("error:", err)
}
}
}()
err = watcher.Add("./configfiles/config.json")
if err != nil {
log.Fatal(err)
}
<-done
}
func main() {
log.Println("Start")
globalConfig, _ = LoadConfig("./configfiles/config.json")
go ConfigWatcher()
for {
log.Println("config:", globalConfig)
time.Sleep(30 * time.Second)
}
}
... إضافته بمثل هذا التكوين:$ cat configfiles/config.json
{
"welcome": "Hello",
"name": "Alice"
}
إذا تم تشغيله ، فسيكون السجل:2020/03/03 22:18:22 config: &{Hello Alice}
2020/03/03 22:18:52 config: &{Hello Alice}
والآن سنقوم بتثبيت هذا التطبيق في Kubernetes ، بعد أن تم تثبيت ConfigMap config في pod بدلاً من الملف من الصورة. تم إعداد مثال على مخطط Helm على GitHub :helm install -n habr-configmap --namespace habr-configmap ./habr-configmap --set 'name.production=Alice' --set 'global.env=production'
وتغيير ConfigMap فقط:- production: "Alice"
+ production: "Bob"
قم بتحديث مخطط Helm في الكتلة ، على سبيل المثال ، مثل:helm upgrade habr-configmap ./habr-configmap --set 'name.production=Bob' --set 'global.env=production'
ماذا سيحدث؟يمكنك إلقاء نظرة على كيفية حل موقف مشابه - تتبع تغييرات ConfigMap - في مشروع أكثر الكبار (وببساطة "حقيقي") هنا .مهم! من المفيد أيضًا أن نتذكر أن كل ما ورد أعلاه في المقالة ينطبق أيضًا على Secret'ov في Kubernetes ( kind: Secret
): ليس لشيء يشبه إلى حد ما ConfigMap ...علاوة! حلول الطرف الثالث
إذا كنت مهتمًا بموضوع تتبع التغييرات في التكوينات ، فهناك بالفعل أدوات مساعدة جاهزة لهذا:- jimmidyson / configmap-reload - يرسل طلب HTTP إذا تغير الملف. يخطط المطور أيضًا لتعليم SIGHUP للإرسال ، ولكن عدم وجود التزامات من أكتوبر 2019 يترك هذه الخطط محل تساؤل.
- stakater / Reloader - يراقب ConfigMap / Secrets ويقوم بالترقية الدورية (كما يسميها مؤلفها) على الموارد المرتبطة بها.
سيكون من الملائم إطلاق مثل هذه التطبيقات مع حاوية جانبية للتطبيقات الموجودة. ومع ذلك ، إذا كنت تعرف ميزات Kubernetes / ConfigMap وتهيئتها للتحرير ليس "مباشرة" (من خلال edit
) ، ولكن فقط كجزء من النشر ... فقد تبدو قدرات هذه الأدوات المساعدة غير ضرورية ، أي تكرار الوظائف الأساسية.استنتاج
مع ظهور ConfigMap في Kubernetes ، انتقلت التكوينات إلى الجولة التالية من التطوير: جلب استخدام محرك القالب لهم مرونة مماثلة لعرض صفحات HTML. لحسن الحظ ، لم تحل مثل هذه المضاعفات محل الحلول القائمة ، ولكنها أصبحت مكملة لها . لذلك ، بالنسبة للمسؤولين (أو بالأحرى حتى المطورين) الذين يعتبرون الميزات الجديدة زائدة عن الحاجة ، لا تزال الملفات القديمة الجيدة متاحة.بالنسبة لأولئك الذين يستخدمون ConfigMap'ami أو ينظرون إليهم ، تقدم المقالة نظرة عامة موجزة عن جوهرها والفروق الدقيقة في استخدامها. إذا كان لديك نصائح وحيل خاصة بك حول هذا الموضوع - يسعدني أن أرى في التعليقات.ملاحظة
اقرأ أيضا في مدونتنا: