النسخ الاحتياطية للدولة في Kubernetes

لذا ، كما يعلم الجميع بالتأكيد ، تم عقد DevOpsConfRussia2018 مؤخرًا في موسكو في "Infraspace" في 1-2 أكتوبر. بالنسبة لأولئك الذين لا يشعرون بالراحة ، يعد DevOpsConf مؤتمرًا محترفًا حول دمج عمليات التطوير والاختبار والتشغيل.


كما شاركت شركتنا في هذا المؤتمر. كنا شركاء ، نمثل الشركة في جناحنا ، وعقدنا أيضًا اجتماعًا صغيرًا. بالمناسبة ، كانت هذه أول مشاركة لنا في هذا النوع من النشاط. المؤتمر الأول ، أول ميتاب ، التجربة الأولى.


عن ماذا تحدثنا؟ كان Mitap حول موضوع "النسخ الاحتياطية في Kubernetes".


على الأرجح سماع هذا الاسم ، سيقول الكثير: "لماذا النسخ الاحتياطي إلى Kubernetes؟ لا تحتاج إلى دعم ، إنها عديمة الجنسية ".



مقدمة ...


لنبدأ بخلفية صغيرة. لماذا أصبح من الضروري تغطية هذا الموضوع ولماذا هو مطلوب.


في عام 2016 ، أصبحنا على دراية بتقنية مثل Kubernetes وبدأنا بتطبيقها بنشاط على مشاريعنا. بالطبع ، هذه مشاريع بشكل أساسي مع بنية الخدمات الصغيرة ، وهذا بدوره يستلزم استخدام عدد كبير من البرامج المتنوعة.


في المشروع الأول ، حيث استخدمنا Kubernetes ، كان لدينا سؤال حول كيفية عمل نسخة احتياطية من خدمات Stateful الموجودة هناك ، والتي تدخل أحيانًا في k8s لسبب أو لآخر.


بدأنا في الدراسة والبحث عن الممارسات الموجودة لحل هذه المشكلة. تواصل مع زملائنا ورفاقنا: "وكيف يتم تنفيذ هذه العملية وبناؤها؟"


بعد الحديث ، أدركنا أن كل هذا يحدث بطرق ووسائل مختلفة ومع عدد كبير من العكازات. ومع ذلك ، لم نتبع أي نهج موحد حتى في إطار مشروع واحد .


لماذا هذا مهم جدا؟ نظرًا لأن مشاريع خدمات شركتنا تعتمد على k8s ، فقد كنا بحاجة فقط إلى تطوير منهجية منظمة لحل هذه المشكلة.


تخيل أنك تعمل في مشروع واحد محدد في كوبر. يحتوي على بعض الخدمات الرسمية وتحتاج إلى نسخ بياناتهم احتياطيًا. من حيث المبدأ ، هنا يمكنك عمل عكازين ونسيانها. ولكن ماذا لو كان لديك بالفعل مشروعين على k8s؟ والمشروع الثاني يستخدم خدمات مختلفة تماما في عمله. وإذا كان هناك بالفعل خمسة مشاريع؟ عشرة؟ أو أكثر من عشرين؟


بالطبع ، وضع العكازات أمر صعب وغير مريح بالفعل. نحن بحاجة إلى نوع من النهج الموحد الذي يمكن استخدامه عند العمل مع العديد من المشاريع في كوبا ، وفي الوقت نفسه ، حتى يتمكن فريق المهندسين بسهولة وبشكل حرفي في غضون دقائق من إجراء التغييرات اللازمة على النسخ الاحتياطية لهذه المشاريع.


في إطار هذه المقالة ، سنتحدث فقط عن الأداة والممارسة التي نستخدمها لحل هذه المشكلة داخل شركتنا.


كيف نفعل هذا؟


Nxs- النسخ الاحتياطي ما هو؟

للنسخ الاحتياطية ، نستخدم أداة المصدر المفتوح الخاصة بنا - nxs-backup. لن ندخل في تفاصيل ما يستطيع. يمكن العثور على مزيد من التفاصيل على الرابط التالي.


الآن دعنا ننتقل إلى تنفيذ النسخ الاحتياطية في k8s. كيف وماذا فعلنا بالضبط.


ما هو النسخ الاحتياطي؟


دعونا نلقي نظرة على نسخة احتياطية من Redmine الخاص بنا. في ذلك ، سوف نقوم بعمل نسخة احتياطية من قاعدة بيانات MySQL وملفات مشروع المستخدم.


كيف نفعل هذا؟


1 CronJob == 1 خدمة

على الخوادم والمجموعات العادية على الأجهزة ، يتم تشغيل معظم أدوات النسخ الاحتياطي تقريبًا من خلال cron العادي. في k8s لهذا الغرض ، نستخدم CronJob's ، أي نقوم بإنشاء 1 CronJob لخدمة واحدة ، والتي سوف نقوم بنسخها احتياطيًا. تقع جميع هذه CronJob s في نفس مساحة الاسم مثل الخدمة نفسها.


لنبدأ بقاعدة بيانات MySQL. لنسخ MySQL احتياطيًا ، نحتاج إلى 4 عناصر ، مثل أي خدمة أخرى تقريبًا:


  • ConfigMap (nxs-backup.conf)
  • ConfigMap (mysql.conf للنسخ الاحتياطي من nxs)
  • السر (يتم تخزين الوصول إلى الخدمة هنا ، في هذه الحالة MySQL). عادة ، يتم تعريف هذا العنصر بالفعل لكي تعمل الخدمة ويمكن إعادة استخدامه.
  • CronJob (لكل خدمة خاصة بها)

دعنا نذهب بالترتيب.


nxs-backup.conf

apiVersion: v1 kind: ConfigMap metadata: name: nxs-backup-conf data: nxs-backup.conf: |- main: server_name: Nixys k8s cluster admin_mail: admins@nixys.ru client_mail: - '' mail_from: backup@nixys.ru level_message: error block_io_read: '' block_io_write: '' blkio_weight: '' general_path_to_all_tmp_dir: /var/nxs-backup cpu_shares: '' log_file: /dev/stdout jobs: !include [conf.d/*.conf] 

نقوم هنا بتعيين المعلمات الأساسية التي يتم تمريرها إلى أداتنا ، وهي ضرورية لتشغيلها. هذا هو اسم الخادم ، والبريد الإلكتروني للإشعارات ، وتقييد استهلاك الموارد وغيرها من المعلمات.


يمكن تحديد التكوينات بتنسيق j2 ، مما يسمح باستخدام متغيرات البيئة.


mysql.conf

 apiVersion: v1 kind: ConfigMap metadata: name: mysql-conf data: service.conf.j2: |- - job: mysql type: mysql tmp_dir: /var/nxs-backup/databases/mysql/dump_tmp sources: - connect: db_host: {{ db_host }} db_port: {{ db_port }} socket: '' db_user: {{ db_user }} db_password: {{ db_password }} target: - redmine_db gzip: yes is_slave: no extra_keys: '--opt --add-drop-database --routines --comments --create-options --quote-names --order-by-primary --hex-blob' storages: - storage: local enable: yes backup_dir: /var/nxs-backup/databases/mysql/dump store: days: 6 weeks: 4 month: 6 

يصف هذا الملف منطق النسخ الاحتياطي للخدمة المقابلة ، في حالتنا هو MySQL.


هنا يمكنك تحديد:


  • ماهو اسم الوظيفة (المجال: الوظيفة)
  • نوع الوظيفة (المجال: النوع)
  • الدليل المؤقت المطلوب لجمع النسخ الاحتياطية (الحقل: tmp_dir)
  • خيارات اتصال MySQL (المجال: اتصال)
  • قاعدة البيانات المطلوب نسخها احتياطيًا (الحقل: الهدف)
  • الحاجة إلى إيقاف Slave قبل الجمع (الحقل: is_slave)
  • مفاتيح إضافية لـ mysqldump (الحقل: extra_keys)
  • تخزين التخزين ، أي التخزين الذي سنقوم بتخزين نسخة منه (الحقل: التخزين)
  • الدليل حيث سنقوم بتخزين نسخنا (الحقل: backup_dir)
  • مخطط التخزين (المجال: مخزن)

في مثالنا ، يتم تعيين نوع التخزين إلى محلي ، أي أننا نقوم بتجميع النسخ الاحتياطية وتخزينها محليًا في دليل محدد للقرنة التي تم إطلاقها.


هنا ، عن طريق القياس مع ملف التكوين هذا ، يمكنك تحديد نفس ملفات التكوين لـ Redis أو PostgreSQL أو أي خدمة ضرورية أخرى ، إذا كانت مدعومة بواسطة أداتنا. يمكن العثور على حقيقة أنه يدعم على الرابط المعطى في وقت سابق.


الخلية السرية

 apiVersion: v1 kind: Secret metadata: name: app-config data: db_name: "" db_host: "" db_user: "" db_password: "" secret_token: "" smtp_address: "" smtp_domain: "" smtp_ssl: "" smtp_enable_starttls_auto: "" smtp_port: "" smtp_auth_type: "" smtp_login: "" smtp_password: "" 

نحتفظ بالوصول السري للاتصال بـ MySQL نفسها وخادم البريد. يمكن تخزينها إما في سر منفصل ، أو الاستفادة من السر الموجود ، بالطبع ، إذا كان هناك سر. لا يوجد شيء مثير للاهتمام هنا. سرنا يحمل أيضًا السر السري ، وهو ضروري لتشغيل Redmine الخاص بنا.


CronJob MySQL

 apiVersion: batch/v1beta1 kind: CronJob metadata: name: mysql spec: schedule: "00 00 * * *" jobTemplate: spec: template: spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - nxs-node5 containers: - name: mysql-backup image: nixyslab/nxs-backup:latest env: - name: DB_HOST valueFrom: secretKeyRef: name: app-config key: db_host - name: DB_PORT value: '3306' - name: DB_USER valueFrom: secretKeyRef: name: app-config key: db_user - name: DB_PASSWORD valueFrom: secretKeyRef: name: app-config key: db_password - name: SMTP_MAILHUB_ADDR valueFrom: secretKeyRef: name: app-config key: smtp_address - name: SMTP_MAILHUB_PORT valueFrom: secretKeyRef: name: app-config key: smtp_port - name: SMTP_USE_TLS value: 'YES' - name: SMTP_AUTH_USER valueFrom: secretKeyRef: name: app-config key: smtp_login - name: SMTP_AUTH_PASS valueFrom: secretKeyRef: name: app-config key: smtp_password - name: SMTP_FROM_LINE_OVERRIDE value: 'NO' volumeMounts: - name: mysql-conf mountPath: /usr/share/nxs-backup/service.conf.j2 subPath: service.conf.j2 - name: nxs-backup-conf mountPath: /etc/nxs-backup/nxs-backup.conf subPath: nxs-backup.conf - name: backup-dir mountPath: /var/nxs-backup imagePullPolicy: Always volumes: - name: mysql-conf configMap: name: mysql-conf items: - key: service.conf.j2 path: service.conf.j2 - name: nxs-backup-conf configMap: name: nxs-backup-conf items: - key: nxs-backup.conf path: nxs-backup.conf - name: backup-dir hostPath: path: /var/backups/k8s type: Directory restartPolicy: OnFailure 

ربما هذا العنصر هو الأكثر إثارة للاهتمام. أولاً ، من أجل تجميع CronJob الصحيح ، تحتاج إلى تحديد مكان تخزين النسخ الاحتياطية المجمعة.


لهذا لدينا خادم منفصل بالعدد الضروري من الموارد. في المثال ، تم تخصيص عقدة نظام مجموعة منفصلة ، nxs-node5 ، لجمع النسخ الاحتياطية. يتم تعيين قيود بدء تشغيل CronJob على العقد التي نحتاجها بواسطة توجيه nodeAffinity.


عند بدء تشغيل CronJob ، يتم توصيل الدليل المقابل به عبر hostPath من النظام المضيف ، والذي يتم استخدامه لتخزين النسخ الاحتياطية.


بعد ذلك ، يتم تكوين ConfigMaps إلى CronJob المحددة ، والتي تحتوي على تكوين nxs-backup ، أي ملفات nxs-backup.conf و mysql.conf التي تحدثنا عنها للتو.


بعد ذلك ، يتم تعيين جميع متغيرات البيئة الضرورية ، والتي يتم تعريفها مباشرة في البيان أو سحبها من Secret'ov.


لذلك ، يتم نقل المتغيرات إلى الحاوية ، ومن خلال docker-entrypoint.sh ، يتم استبدالها في ConfigMaps في الأماكن التي نحتاجها بالقيم الضرورية. بالنسبة لـ MySQL ، هذه هي db_host و db_user و db_password. في هذه الحالة ، نقوم بتمرير المنفذ ببساطة كقيمة في بيان CronJob ، لأنه لا يحمل أي معلومات قيمة.


حسنًا ، مع MySQL ، يبدو كل شيء واضحًا. الآن دعونا نرى ما هو مطلوب لعمل نسخة احتياطية من ملفات تطبيق Redmine.


desc_files.conf

 apiVersion: v1 kind: ConfigMap metadata: name: desc-files-conf data: service.conf.j2: |- - job: desc-files type: desc_files tmp_dir: /var/nxs-backup/files/desc/dump_tmp sources: - target: - /var/www/files gzip: yes storages: - storage: local enable: yes backup_dir: /var/nxs-backup/files/desc/dump store: days: 6 weeks: 4 month: 6 

هذا ملف تكوين يصف منطق النسخ الاحتياطي للملفات. لا يوجد شيء غير معتاد هنا أيضًا ، فقد تم تعيين جميع المعلمات نفسها كما هو الحال مع MySQL ، باستثناء بيانات التفويض ، لأنها ببساطة ليست كذلك. على الرغم من أنها يمكن أن تكون ، إذا كانت البروتوكولات لنقل البيانات متضمنة: ssh و ftp و webdav و s3 وغيرها. سننظر في هذا الخيار بعد ذلك بقليل.


CronJob desc_files

 apiVersion: batch/v1beta1 kind: CronJob metadata: name: desc-files spec: schedule: "00 00 * * *" jobTemplate: spec: template: spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - nxs-node5 containers: - name: desc-files-backup image: nixyslab/nxs-backup:latest env: - name: SMTP_MAILHUB_ADDR valueFrom: secretKeyRef: name: app-config key: smtp_address - name: SMTP_MAILHUB_PORT valueFrom: secretKeyRef: name: app-config key: smtp_port - name: SMTP_USE_TLS value: 'YES' - name: SMTP_AUTH_USER valueFrom: secretKeyRef: name: app-config key: smtp_login - name: SMTP_AUTH_PASS valueFrom: secretKeyRef: name: app-config key: smtp_password - name: SMTP_FROM_LINE_OVERRIDE value: 'NO' volumeMounts: - name: desc-files-conf mountPath: /usr/share/nxs-backup/service.conf.j2 subPath: service.conf.j2 - name: nxs-backup-conf mountPath: /etc/nxs-backup/nxs-backup.conf subPath: nxs-backup.conf - name: target-dir mountPath: /var/www/files - name: backup-dir mountPath: /var/nxs-backup imagePullPolicy: Always volumes: - name: desc-files-conf configMap: name: desc-files-conf items: - key: service.conf.j2 path: service.conf.j2 - name: nxs-backup-conf configMap: name: nxs-backup-conf items: - key: nxs-backup.conf path: nxs-backup.conf - name: backup-dir hostPath: path: /var/backups/k8s type: Directory - name: target-dir persistentVolumeClaim: claimName: redmine-app-files restartPolicy: OnFailure 

لا شيء جديد سواء بخصوص MySQL. ولكن هنا يتم تركيب PV (هدف) إضافي ، والذي سنقوم بعمل نسخة احتياطية منه - / var / www / files. خلاف ذلك ، كل شيء هو نفسه ، نقوم بتخزين النسخ محليًا على العقدة التي نحتاجها ، والتي يتم تعيين CronJob لها.


الملخص

لكل خدمة نريد نسخها احتياطيًا ، نقوم بإنشاء CronJob منفصل مع جميع العناصر الضرورية ذات الصلة: ConfigMaps و Secrets. قياسا على الأمثلة المدروسة ، يمكننا عمل نسخة احتياطية من أي خدمة مماثلة في المجموعة.


أعتقد ، بناءً على هذين المثالين ، لدى كل شخص فكرة عن كيفية دعم خدمات الدولة في كوبا. أعتقد أنه لا معنى لتحليل نفس الأمثلة بالتفصيل للخدمات الأخرى ، لأنها في الأساس كلها متشابهة ولديها اختلافات طفيفة.


في الواقع ، هذا ما أردنا تحقيقه ، وهو نوع من النهج الموحد لبناء عملية النسخ الاحتياطي. بحيث يمكن تطبيق هذا النهج على عدد كبير من المشاريع المختلفة على أساس k8s.


أين نقوم بتخزينه؟


في جميع الأمثلة التي نوقشت أعلاه ، نقوم بتخزين النسخ في الدليل المحلي للعقدة التي تعمل الحاوية عليها. ولكن لا أحد يزعج توصيل وحدة التخزين الثابتة كوحدة تخزين خارجية فعالة وجمع النسخ هناك. أو يمكنك فقط مزامنتها مع متجر بعيد باستخدام البروتوكول المطلوب دون حفظ محليًا. أي أن هناك الكثير من الاختلافات. اجمع أولاً محليًا ، ثم قم بالمزامنة. أو لتجميع وتخزين فقط على مخزن بعيد ، إلخ. التكوين مرن للغاية.


mysql.conf + s3

فيما يلي مثال لملف تكوين النسخ الاحتياطي MySQL ، حيث يتم تخزين النسخ محليًا على العقدة التي يعمل فيها CronJob ، وتتم مزامنتها أيضًا في s3.


 apiVersion: v1 kind: ConfigMap metadata: name: mysql-conf data: service.conf.j2: |- - job: mysql type: mysql tmp_dir: /var/nxs-backup/databases/mysql/dump_tmp sources: - connect: db_host: {{ db_host }} db_port: {{ db_port }} socket: '' db_user: {{ db_user }} db_password: {{ db_password }} target: - redmine_db gzip: yes is_slave: no extra_keys: ' --opt --add-drop-database --routines --comments --create-options --quote-names --order-by-primary --hex-blob' storages: - storage: local enable: yes backup_dir: /var/nxs-backup/databases/mysql/dump store: days: 6 weeks: 4 month: 6 - storage: s3 enable: yes backup_dir: /nxs-backup/databases/mysql/dump bucket_name: {{ bucket_name }} access_key_id: {{ access_key_id }} secret_access_key: {{ secret_access_key }} s3fs_opts: {{ s3fs_opts }} store: days: 2 weeks: 1 month: 6 

أي إذا لم يكن تخزين النُسخ محليًا كافيًا ، فيمكنك مزامنتها مع أي تخزين بعيد باستخدام البروتوكول المناسب. يمكن أن يكون عدد التخزين للتخزين أي.


ولكن في هذه الحالة ، ما زلت بحاجة إلى إجراء بعض التغييرات الإضافية ، وهي:


  • قم بتوصيل ConfigMap المناسب بالمحتوى الضروري للتفويض باستخدام AWS S3 ، بتنسيق j2
  • أنشئ سرًا مناسبًا لتخزين إذن الوصول
  • تعيين متغيرات البيئة اللازمة المأخوذة من السر أعلاه
  • اضبط docker-entrypoint.sh لاستبدال المتغيرات المقابلة في ConfigMap
  • إعادة بناء صورة Docker ، وإضافة أدوات للعمل مع AWS S3

في حين أن هذه العملية بعيدة عن الكمال ، لكننا نعمل على ذلك. لذلك ، في المستقبل القريب ، سنضيف إلى nxs-backup القدرة على تحديد المعلمات في ملف التكوين باستخدام متغيرات البيئة ، والتي ستبسط بشكل كبير العمل مع ملف نقطة الدخول وتقليل الوقت المستغرق في إضافة دعم النسخ الاحتياطي للخدمات الجديدة.


الخلاصة


ربما هذا كل شيء.


باستخدام النهج الذي تحدثنا عنه للتو ، أولاً وقبل كل شيء ، يسمح لنا بتنظيم وتقديم خدمات النسخ الاحتياطي للمشروع إلى k8s. أي أن هذا حل جاهز ، والأهم من ذلك ، الممارسة التي يمكنك تطبيقها في مشاريعك ، مع عدم إضاعة الوقت والجهد في البحث عن حلول مفتوحة المصدر وتحسينها.

Source: https://habr.com/ru/post/ar426543/


All Articles