Jadi, seperti yang diketahui semua orang, DevOpsConfRussia2018 baru-baru ini diadakan di Moskow di “Infraspace” pada 1-2 Oktober. Bagi mereka yang tidak nyaman , DevOpsConf adalah konferensi profesional tentang integrasi pengembangan, pengujian dan proses operasi.
Perusahaan kami juga ikut serta dalam konferensi ini. Kami adalah mitranya, mewakili perusahaan di stan kami, dan juga mengadakan pertemuan kecil. Ngomong-ngomong, ini adalah partisipasi pertama kami dalam kegiatan semacam ini. Konferensi pertama, mitap pertama, pengalaman pertama.
Apa yang kita bicarakan? Mitap berada di topik "Backups in Kubernetes".
Kemungkinan besar mendengar nama ini, banyak yang akan mengatakan: "Mengapa kembali ke Kubernet? Tidak perlu didukung, itu Stateless. "

Pendahuluan ...
Mari kita mulai dengan sedikit latar belakang. Mengapa menjadi penting untuk membahas topik ini dan mengapa diperlukan.
Pada tahun 2016, kami berkenalan dengan teknologi seperti Kubernetes dan mulai secara aktif menerapkannya pada proyek-proyek kami. Tentu saja, ini terutama proyek-proyek dengan arsitektur microservice, dan ini pada gilirannya memerlukan penggunaan sejumlah besar perangkat lunak yang beragam.
Dengan proyek pertama di mana kami menggunakan Kubernetes, kami memiliki pertanyaan tentang cara mencadangkan layanan Stateful yang berlokasi di sana, yang terkadang karena suatu alasan atau lainnya jatuh ke dalam k8s.
Kami mulai mempelajari dan mencari praktik yang ada untuk menyelesaikan masalah ini. Berkomunikasi dengan kolega dan kawan kami: "Dan bagaimana proses ini dilakukan dan dibangun oleh mereka?"
Setelah berbicara, kami menyadari bahwa untuk semua ini terjadi dengan metode, cara, dan dengan sejumlah besar kruk. Namun, kami tidak mengikuti pendekatan terpadu apa pun bahkan dalam kerangka satu proyek .
Mengapa ini sangat penting? Karena proyek layanan perusahaan kami didasarkan pada k8, kami hanya perlu mengembangkan metodologi terstruktur untuk menyelesaikan masalah ini.
Bayangkan Anda bekerja dengan satu proyek khusus di Coober. Ini berisi beberapa layanan Stateful dan Anda perlu membuat cadangan data mereka. Pada prinsipnya, di sini Anda dapat melakukan beberapa kruk dan melupakannya. Tetapi bagaimana jika Anda sudah memiliki dua proyek pada K8? Dan proyek kedua menggunakan layanan yang sama sekali berbeda dalam pekerjaannya. Dan jika sudah ada lima proyek? Sepuluh? Atau lebih dari dua puluh?
Tentu saja, mengenakan kruk sudah sulit dan tidak nyaman. Kami membutuhkan semacam pendekatan terpadu yang dapat digunakan ketika bekerja dengan banyak proyek di Kuba, dan pada saat yang sama, sehingga tim insinyur dapat dengan mudah dan harfiah dalam hitungan menit membuat perubahan yang diperlukan untuk cadangan proyek-proyek ini.
Dalam kerangka kerja artikel ini, kami hanya akan berbicara tentang alat apa dan praktik apa yang kami gunakan untuk menyelesaikan masalah ini di perusahaan kami.
Bagaimana kita melakukan ini?
Nxs-backup apa itu?
Untuk cadangan, kami menggunakan alat open source kami sendiri - nxs-backup. Kami tidak akan membahas detail apa yang dia bisa. Rincian lebih lanjut dapat ditemukan di tautan berikut.
Sekarang mari kita beralih ke implementasi backup di k8s. Bagaimana dan apa sebenarnya yang dilakukan oleh kami.
Apa itu cadangan?
Mari kita lihat contoh cadangan Redmine kita sendiri. Di dalamnya, kami akan membuat cadangan database MySQL dan file proyek pengguna.
Bagaimana kita melakukan ini?
1 CronJob == 1 Layanan
Pada server biasa dan cluster pada perangkat keras, hampir semua alat cadangan sebagian besar diluncurkan melalui cron biasa. Dalam k8s untuk tujuan ini kita menggunakan CronJob, mis. Kita membuat 1 CronJob untuk 1 layanan, yang akan kita backup. Semua CronJob ini terletak di namespace yang sama dengan layanan itu sendiri.
Mari kita mulai dengan database MySQL. Untuk membuat cadangan MySQL, kami membutuhkan 4 elemen, seperti untuk hampir semua layanan lain:
- ConfigMap (nxs-backup.conf)
- ConfigMap (mysql.conf untuk nxs-backup)
- Rahasia (akses ke layanan disimpan di sini, dalam hal ini MySQL). Biasanya, elemen ini sudah ditentukan untuk layanan untuk bekerja dan dapat digunakan kembali.
- CronJob (untuk setiap layanannya sendiri)
Mari kita mulai.
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
Di sini kita mengatur parameter dasar yang dilewatkan ke alat kita, yang diperlukan untuk operasinya. Ini adalah nama server, email untuk notifikasi, pembatasan konsumsi sumber daya dan parameter lainnya.
Konfigurasi dapat ditentukan dalam format j2, yang memungkinkan penggunaan variabel lingkungan.
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
File ini menjelaskan logika cadangan untuk layanan terkait, dalam kasus kami ini adalah MySQL.
Di sini Anda dapat menentukan:
- Apa nama Pekerjaan (bidang: pekerjaan)
- Jenis pekerjaan (bidang: jenis)
- Direktori sementara diperlukan untuk mengumpulkan cadangan (bidang: tmp_dir)
- Opsi koneksi MySQL (bidang: hubungkan)
- Basis data yang akan didukung (bidang: target)
- Perlu menghentikan Slave sebelum mengumpulkan (bidang: is_slave)
- Kunci tambahan untuk mysqldump (bidang: extra_keys)
- Penyimpanan penyimpanan, mis. Di penyimpanan mana kami akan menyimpan salinan (bidang: penyimpanan)
- Direktori tempat kami akan menyimpan salinan kami (bidang: backup_dir)
- Skema penyimpanan (bidang: toko)
Dalam contoh kami, jenis penyimpanan diatur ke lokal, mis. Kami mengumpulkan dan menyimpan cadangan secara lokal di direktori tertentu dari pod yang diluncurkan.
Di sini, dengan analogi dengan file konfigurasi ini, Anda dapat menentukan file konfigurasi yang sama untuk Redis, PostgreSQL atau layanan lain yang diperlukan, jika didukung oleh alat kami. Fakta bahwa itu mendukung dapat ditemukan pada tautan yang diberikan sebelumnya.
Mysql rahasia
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: ""
Kami menjaga akses rahasia untuk terhubung ke MySQL itu sendiri dan server mail. Mereka dapat disimpan baik dalam rahasia terpisah, atau mengambil keuntungan dari yang sudah ada, tentu saja, jika ada. Tidak ada yang menarik di sini. Rahasia kami juga menyimpan secret_token, yang diperlukan untuk pengoperasian Redmine kami.
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
Mungkin elemen ini yang paling menarik. Pertama, untuk mengkompilasi CronJob yang benar, Anda harus menentukan di mana cadangan yang dikumpulkan akan disimpan.
Untuk ini kami memiliki server terpisah dengan jumlah sumber daya yang diperlukan. Dalam contoh ini, node gugus terpisah, nxs-node5, dialokasikan untuk mengumpulkan cadangan. Pembatasan memulai CronJob pada node yang kita butuhkan diatur oleh direktif nodeAffinity.
Ketika CronJob dijalankan, direktori terkait terhubung dengannya melalui hostPath dari sistem host, yang digunakan untuk menyimpan salinan cadangan.
Selanjutnya, ConfigMaps terhubung ke CronJob tertentu, yang berisi konfigurasi untuk nxs-backup, yaitu, file nxs-backup.conf dan mysql.conf yang baru saja kita bicarakan di atas.
Kemudian, semua variabel lingkungan yang diperlukan diatur, yang didefinisikan langsung dalam manifes atau ditarik dari Secret'ov.
Jadi, variabel ditransfer ke wadah dan, melalui docker-entrypoint.sh, diganti di ConfigMaps di tempat yang kita butuhkan dengan nilai yang diperlukan. Untuk MySQL, ini adalah db_host, db_user, db_password. Dalam hal ini, kami melewatkan port hanya sebagai nilai dalam manifes CronJob, karena ia tidak membawa informasi berharga.
Nah, dengan MySQL semuanya tampak jelas. Sekarang mari kita lihat apa yang diperlukan untuk mencadangkan file aplikasi 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
Ini adalah file konfigurasi yang menjelaskan logika cadangan untuk file. Tidak ada yang tidak biasa di sini, semua parameter yang sama ditetapkan untuk MySQL, dengan pengecualian data otorisasi, karena memang tidak. Meskipun bisa, jika protokol untuk transfer data terlibat: ssh, ftp, webdav, s3 dan lainnya. Kami akan mempertimbangkan opsi ini nanti.
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
Tidak ada yang baru, baik, tentang MySQL. Tapi di sini satu PV tambahan (target-dir) dipasang, yang akan kita buat cadangannya - / var / www / file. Kalau tidak, semuanya sama, kita menyimpan salinan secara lokal pada simpul yang kita butuhkan, yang ditugaskan CronJob.
Ringkasan
Untuk setiap layanan yang ingin kami backup, kami membuat CronJob terpisah dengan semua elemen terkait yang diperlukan: ConfigMaps dan Secrets. Dengan analogi dengan contoh-contoh yang dipertimbangkan, kami dapat membuat cadangan layanan serupa di cluster.
Saya pikir, berdasarkan dua contoh ini, setiap orang memiliki ide bagaimana kami mendukung layanan Stateful di Kuba. Saya pikir tidak masuk akal untuk menganalisis secara rinci contoh yang sama untuk layanan lain, karena pada dasarnya mereka semua sama dan memiliki sedikit perbedaan.
Sebenarnya, inilah yang ingin kami capai, yaitu, semacam pendekatan terpadu untuk membangun proses pencadangan. Dan agar pendekatan ini dapat diterapkan pada sejumlah besar proyek yang berbeda berdasarkan pada K8.
Di mana kita menyimpannya?
Dalam semua contoh yang dibahas di atas, kami menyimpan salinan di direktori lokal dari node di mana wadah berjalan. Tapi tidak ada yang mengganggu untuk menghubungkan Volume Persisten sebagai penyimpanan eksternal yang berfungsi dan mengumpulkan salinan di sana. Atau Anda hanya dapat menyinkronkannya ke toko jarak jauh menggunakan protokol yang diinginkan tanpa menyimpan secara lokal. Artinya, ada banyak variasi. Pertama kumpulkan secara lokal, lalu sinkronkan. Atau untuk mengumpulkan dan menyimpan hanya di penyimpanan jarak jauh, dll. Konfigurasi ini cukup fleksibel.
mysql.conf + s3
Berikut ini adalah contoh file konfigurasi cadangan MySQL, di mana salinan disimpan secara lokal pada node tempat CronJob berjalan, dan juga disinkronkan dalam 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
Artinya, jika tidak cukup untuk menyimpan salinan secara lokal, Anda dapat menyinkronkannya ke penyimpanan jarak jauh menggunakan protokol yang sesuai. Jumlah Penyimpanan untuk penyimpanan bisa berapa saja.
Namun dalam hal ini, Anda masih perlu membuat beberapa perubahan tambahan, yaitu:
- Hubungkan ConfigMap yang sesuai dengan konten yang diperlukan untuk otorisasi dengan AWS S3, dalam format j2
- Buat Rahasia yang sesuai untuk menyimpan akses otorisasi
- Tetapkan variabel lingkungan yang diperlukan yang diambil dari Rahasia di atas
- Sesuaikan docker-entrypoint.sh untuk mengganti variabel yang sesuai di ConfigMap
- Bangun kembali gambar Docker, tambahkan utilitas untuk bekerja dengan AWS S3
Meskipun proses ini jauh dari sempurna, tetapi kami sedang mengusahakannya. Oleh karena itu, dalam waktu dekat kami akan menambah nxs-backup kemampuan untuk menentukan parameter dalam file konfigurasi menggunakan variabel lingkungan, yang akan sangat menyederhanakan pekerjaan dengan file entrypoint dan meminimalkan waktu yang dihabiskan untuk menambahkan dukungan untuk cadangan layanan baru.
Kesimpulan
Itu mungkin saja.
Menggunakan pendekatan yang baru saja kita bicarakan, pertama-tama, memungkinkan kita untuk menyusun dan membuat cadangan layanan proyek Stateful ke k8s. Artinya, ini adalah solusi yang siap pakai, dan yang paling penting, praktik yang dapat Anda terapkan dalam proyek Anda, tanpa membuang waktu dan upaya untuk mencari dan memperbarui solusi open source yang ada.