Dengan artikel ini, kami membuka serangkaian publikasi dengan instruksi praktis tentang bagaimana membuat hidup lebih mudah bagi kami (operasi) dan pengembang dalam berbagai situasi yang terjadi secara harfiah setiap hari. Semuanya dikumpulkan dari pengalaman nyata dalam memecahkan masalah dari klien dan telah meningkat dari waktu ke waktu, tetapi masih tidak mengklaim sebagai ideal - menganggapnya lebih sebagai ide dan kosong.
Saya akan mulai dengan "trik" dalam mempersiapkan dump database besar seperti MySQL dan PostgreSQL untuk penyebaran cepat mereka untuk berbagai kebutuhan - terutama, pada platform untuk pengembang. Konteks operasi yang dijelaskan di bawah ini adalah lingkungan khas kami, yang meliputi kluster Kubernet yang berfungsi dan penggunaan GitLab (dan 
dapp ) untuk CI / CD. Ayo pergi!

Kesulitan utama dalam Kubernetes ketika menggunakan fitur cabang adalah database besar, ketika pengembang ingin menguji / menunjukkan perubahan mereka pada database penuh (atau hampir lengkap) dari produksi. Sebagai contoh:
- Ada aplikasi dengan database di MySQL untuk 1 TB dan 10 pengembang yang mengembangkan fitur mereka sendiri.
- Pengembang menginginkan loop tes individual dan beberapa loop lebih spesifik untuk tes dan / atau demo.
- Selain itu, ada kebutuhan untuk mengembalikan dump malam dari basis produksi di sirkuit pengujian untuk waktu yang waras - untuk mereproduksi masalah dengan klien atau bug.
- Akhirnya, dimungkinkan untuk meringankan ukuran database setidaknya 150 GB - tidak begitu banyak, tetapi masih menghemat ruang. Yaitu kita masih perlu entah bagaimana mempersiapkan tempat sampah.
Catatan : Biasanya kita mencadangkan basis data MySQL menggunakan innobackupex Percona, yang memungkinkan kita menyimpan semua basis data dan pengguna ... - singkatnya, semua yang mungkin diperlukan. Ini adalah contoh yang dipertimbangkan lebih lanjut dalam artikel, meskipun dalam kasus umum tidak masalah bagaimana Anda membuat cadangan.Jadi, katakanlah kita memiliki cadangan basis data. Apa yang harus dilakukan selanjutnya?
Langkah 1: Mempersiapkan database baru dari dump
Pertama-tama, kita akan membuat dalam 
Penyebaran Kubernetes, yang akan terdiri dari dua kontainer init 
(mis., Wadah khusus yang berjalan sebelum perapian aplikasi dan memungkinkan Anda untuk melakukan pra-konfigurasi) dan satu perapian.
Tapi di mana menempatkannya? Kami memiliki basis data besar (1 TB) dan kami ingin meningkatkan sepuluh instansnya - kami membutuhkan server dengan disk besar (10+ TB). Kami memesannya secara terpisah untuk tugas ini dan menandai simpul dengan server ini dengan 
label khusus yang 
dedicated: non-prod-db . Pada saat yang sama, kita akan menggunakan eponymous taint, yang akan dikatakan Kubernetes bahwa hanya aplikasi yang resisten (memiliki 
toleransi ) yang dapat bergulir ke simpul ini, mis., Menerjemahkan Kubernetes ke dalam bahasa, 
dedicated Equal non-prod-db .
Menggunakan 
nodeSelector dan 
tolerations pilih node yang diinginkan (terletak di server dengan disk besar):
  nodeSelector: dedicated: non-prod-db tolerations: - key: "dedicated" operator: "Equal" value: "non-prod-db" effect: "NoExecute" 
... dan ambil uraian isi simpul ini.
Kontainer Init: get-bindump
Wadah init pertama yang akan kita sebut 
get-bindump . 
emptyDir (in 
/var/lib/mysql ), di mana dump basis data yang diterima dari server cadangan akan ditambahkan. Untuk melakukan ini, wadah memiliki semua yang Anda butuhkan: kunci SSH, alamat server cadangan. Tahap ini dalam kasus kami membutuhkan waktu sekitar 2 jam.
Deskripsi wadah ini di 
Penempatan adalah sebagai berikut:
  - name: get-bindump image: db-dumps imagePullPolicy: Always command: [ "/bin/sh", "-c", "/get_bindump.sh" ] resources: limits: memory: "5000Mi" cpu: "1" requests: memory: "5000Mi" cpu: "1" volumeMounts: - name: dump mountPath: /dump - name: mysqlbindir mountPath: /var/lib/mysql - name: id-rsa mountPath: /root/.ssh 
Skrip 
get_bindump.sh digunakan dalam wadah:
 
Kontainer Init: persiapan-bindump
Setelah mengunduh cadangan, wadah init kedua diluncurkan - 
prepare-bindump . Ini mengeksekusi 
innobackupex --apply-log (karena file sudah tersedia di 
/var/lib/mysql - berkat 
emptyDir dari 
get-bindump ) dan server MySQL mulai.
Dalam wadah init inilah kita melakukan semua konversi yang diperlukan ke basis data, menyiapkannya untuk aplikasi yang dipilih: kita membersihkan tabel yang diizinkan, mengubah akses di dalam basis data, dll. Kemudian kita mematikan server MySQL dan cukup mengarsipkan seluruh 
/var/lib/mysql ke file tar.gz. Sebagai hasilnya, dump cocok dengan file 100 GB, yang sudah menjadi urutan besarnya lebih kecil dari 1 TB asli. Tahap ini memakan waktu sekitar 5 jam.
Deskripsi wadah init kedua dalam 
Penerapan :
  - name: prepare-bindump image: db-dumps imagePullPolicy: Always command: [ "/bin/sh", "-c", "/prepare_bindump.sh" ] resources: limits: memory: "5000Mi" cpu: "1" requests: memory: "5000Mi" cpu: "1" volumeMounts: - name: dump mountPath: /dump - name: mysqlbindir mountPath: /var/lib/mysql - name: debian-cnf mountPath: /etc/mysql/debian.cnf subPath: debian.cnf 
Skrip 
prepare_bindump.sh digunakan di dalamnya terlihat seperti ini:
 
Di bawah
Akord terakhir adalah peluncuran perapian utama, yang terjadi setelah kontainer init dieksekusi. Di pod, kami memiliki nginx sederhana, dan melalui 
emtpyDir terkompresi dan terpangkas 100 GB 
emtpyDir . Fungsi nginx ini adalah untuk memberikan dump ini.
Konfigurasi perapian:
  - name: nginx image: nginx:alpine resources: requests: memory: "1500Mi" cpu: "400m" lifecycle: preStop: exec: command: ["/usr/sbin/nginx", "-s", "quit"] livenessProbe: httpGet: path: /healthz port: 80 scheme: HTTP timeoutSeconds: 7 failureThreshold: 5 volumeMounts: - name: dump mountPath: /usr/share/nginx/html - name: nginx-config mountPath: /etc/nginx/nginx.conf subPath: nginx.conf readOnly: false volumes: - name: dump emptyDir: {} - name: mysqlbindir emptyDir: {} 
Inilah tampilan seluruh Penempatan dengan initContainers ... --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: db-dumps spec: strategy: rollingUpdate: maxUnavailable: 0 revisionHistoryLimit: 2 template: metadata: labels: app: db-dumps spec: imagePullSecrets: - name: regsecret nodeSelector: dedicated: non-prod-db tolerations: - key: "dedicated" operator: "Equal" value: "non-prod-db" effect: "NoExecute" initContainers: - name: get-bindump image: db-dumps imagePullPolicy: Always command: [ "/bin/sh", "-c", "/get_bindump.sh" ] resources: limits: memory: "5000Mi" cpu: "1" requests: memory: "5000Mi" cpu: "1" volumeMounts: - name: dump mountPath: /dump - name: mysqlbindir mountPath: /var/lib/mysql - name: id-rsa mountPath: /root/.ssh - name: prepare-bindump image: db-dumps imagePullPolicy: Always command: [ "/bin/sh", "-c", "/prepare_bindump.sh" ] resources: limits: memory: "5000Mi" cpu: "1" requests: memory: "5000Mi" cpu: "1" volumeMounts: - name: dump mountPath: /dump - name: mysqlbindir mountPath: /var/lib/mysql - name: log mountPath: /var/log/mysql - name: debian-cnf mountPath: /etc/mysql/debian.cnf subPath: debian.cnf containers: - name: nginx image: nginx:alpine resources: requests: memory: "1500Mi" cpu: "400m" lifecycle: preStop: exec: command: ["/usr/sbin/nginx", "-s", "quit"] livenessProbe: httpGet: path: /healthz port: 80 scheme: HTTP timeoutSeconds: 7 failureThreshold: 5 volumeMounts: - name: dump mountPath: /usr/share/nginx/html - name: nginx-config mountPath: /etc/nginx/nginx.conf subPath: nginx.conf readOnly: false volumes: - name: dump emptyDir: {} - name: mysqlbindir emptyDir: {} - name: log emptyDir: {} - name: id-rsa secret: defaultMode: 0600 secretName: somedb-id-rsa - name: nginx-config configMap: name: somedb-nginx-config - name: debian-cnf configMap: name: somedb-debian-cnf --- apiVersion: v1 kind: Service metadata: name: somedb-db-dump spec: clusterIP: None selector: app: db-dumps ports: - name: http port: 80 
 Catatan tambahan:
- Dalam kasus kami, kami menyiapkan tempat pembuangan baru setiap malam menggunakan pekerjaan yang dijadwalkan di GitLab. Yaitu setiap malam, Penyebaran ini secara otomatis diluncurkan, yang menarik dump baru dan menyiapkannya untuk distribusi ke semua lingkungan pengembang pengujian.
- Mengapa kita juga membuang volume /dumpke dalam wadah init (dan di dalam skrip terdapat pemeriksaan untuk keberadaan/dump/version.txt)? Ini dilakukan jika server yang dijalankannya di-restart. Kontainer akan mulai lagi dan tanpa pemeriksaan ini, dump akan mulai mengunduh lagi. Jika kita sudah menyiapkan dump sekali, maka pada awal berikutnya (jika server reboot),/dump/version.txtflag/dump/version.txtakan menginformasikan tentang ini.
- Apa gambar db-dumps? Kami mengumpulkannya dengan dapp dan Dappfile-nya terlihat seperti ini:
 
  dimg: "db-dumps" from: "ubuntu:16.04" docker: ENV: TERM: xterm ansible: beforeInstall: - name: "Install percona repositories" apt: deb: https://repo.percona.com/apt/percona-release_0.1-4.xenial_all.deb - name: "Add repository for borgbackup" apt_repository: repo="ppa:costamagnagianfranco/borgbackup" codename="xenial" update_cache=yes - name: "Add repository for mysql 5.6" apt_repository: repo: deb http://archive.ubuntu.com/ubuntu trusty universe state: present update_cache: yes - name: "Install packages" apt: name: "{{`{{ item }}`}}" state: present with_items: - openssh-client - mysql-server-5.6 - mysql-client-5.6 - borgbackup - percona-xtrabackup-24 setup: - name: "Add get_bindump.sh" copy: content: | {{ .Files.Get ".dappfiles/get_bindump.sh" | indent 8 }} dest: /get_bindump.sh mode: 0755 - name: "Add prepare_bindump.sh" copy: content: | {{ .Files.Get ".dappfiles/prepare_bindump.sh" | indent 8 }} dest: /prepare_bindump.sh mode: 0755
 
Langkah 2: Meluncurkan basis data di lingkungan pengembang
Saat meluncurkan basis data MySQL di lingkungan pengujian pengembang, ia memiliki tombol di GitLab yang meluncurkan penempatan kembali 
Penempatan dengan MySQL dengan 
RollingUpdate.maxUnavailable: 0 yang tersedia 
RollingUpdate.maxUnavailable: 0 :

Bagaimana ini diterapkan?Di GitLab, ketika Anda mengklik pada 
reload db , 
Deployment dengan spesifikasi berikut digunakan:
 spec: strategy: rollingUpdate: maxUnavailable: 0 
Yaitu kami memberi tahu Kubernetes untuk memperbarui 
Penempatan (buat yang baru di bawah) dan pastikan bahwa setidaknya satu di bawah adalah siaran langsung. Karena ketika membuat perapian baru, ia memiliki wadah init saat mereka bekerja, yang baru 
tidak masuk ke status 
Running , yang berarti bahwa yang lama terus bekerja. Dan hanya pada saat MySQL itu sendiri mulai (dan probe kesiapan bekerja), lalu lintas beralih ke sana, dan yang lama (dengan database lama) dihapus.
Detail tentang skema ini dapat ditemukan dalam materi berikut:
 Pendekatan yang dipilih memungkinkan kita untuk menunggu sampai dump baru diunduh, dibuka ritsleting dan diluncurkan, dan hanya setelah itu dump yang lama akan dihapus dari MySQL. Jadi, saat kami sedang mempersiapkan tempat pembuangan baru, kami bekerja dengan diam-diam dengan pangkalan lama.
Wadah init dari 
Penempatan ini menggunakan perintah berikut:
 curl "$DUMP_URL" | tar -C /var/lib/mysql/ -xvz 
Yaitu kami mengunduh dump database terkompresi yang disiapkan pada langkah 1, unzip ke 
/var/lib/mysql , dan kemudian mulai di bawah 
Penempatan , di mana MySQL diluncurkan dengan data yang sudah disiapkan. Semua ini memakan waktu sekitar 2 jam.
Dan Penempatan adalah sebagai berikut ... apiVersion: apps/v1beta1 kind: Deployment metadata: name: mysql spec: strategy: rollingUpdate: maxUnavailable: 0 template: metadata: labels: service: mysql spec: imagePullSecrets: - name: regsecret nodeSelector: dedicated: non-prod-db tolerations: - key: "dedicated" operator: "Equal" value: "non-prod-db" effect: "NoExecute" initContainers: - name: getdump image: mysql-with-getdump command: ["/usr/local/bin/getdump.sh"] resources: limits: memory: "6000Mi" cpu: "1.5" requests: memory: "6000Mi" cpu: "1.5" volumeMounts: - mountPath: /var/lib/mysql name: datadir - mountPath: /etc/mysql/debian.cnf name: debian-cnf subPath: debian.cnf env: - name: DUMP_URL value: "http://somedb-db-dump.infra-db.svc.cluster.local/mysql_bindump.tar.gz" containers: - name: mysql image: mysql:5.6 resources: limits: memory: "1024Mi" cpu: "1" requests: memory: "1024Mi" cpu: "1" lifecycle: preStop: exec: command: ["/etc/init.d/mysql", "stop"] ports: - containerPort: 3306 name: mysql protocol: TCP volumeMounts: - mountPath: /var/lib/mysql name: datadir - mountPath: /etc/mysql/debian.cnf name: debian-cnf subPath: debian.cnf env: - name: MYSQL_ROOT_PASSWORD value: "password" volumes: - name: datadir emptyDir: {} - name: debian-cnf configMap: name: somedb-debian-cnf --- apiVersion: v1 kind: Service metadata: name: mysql spec: clusterIP: None selector: service: mysql ports: - name: mysql port: 3306 protocol: TCP --- apiVersion: v1 kind: ConfigMap metadata: name: somedb-debian-cnf data: debian.cnf: | [client] host = localhost user = debian-sys-maint password = password socket = /var/run/mysqld/mysqld.sock [mysql_upgrade] host = localhost user = debian-sys-maint password = password socket = /var/run/mysqld/mysqld.sock 
 Ringkasan
Ternyata kami selalu memiliki 
Penyebaran , yang diluncurkan setiap malam dan melakukan hal berikut:
- Mendapat dump database baru
- entah bagaimana ia mempersiapkannya untuk operasi yang benar dalam lingkungan pengujian (misalnya, trankeytit beberapa tabel, menggantikan data pengguna nyata, membuat pengguna yang diperlukan, dll.);
- memberikan setiap pengembang kesempatan untuk meluncurkan basis data yang telah disiapkan ke namespace mereka di Deployment dengan menekan tombol di CI - berkat Layanan yang tersedia di dalamnya, basis data akan tersedia di mysql(misalnya, mungkin itu adalah nama layanan di namespace).
Sebagai contoh yang kami teliti, membuat dump dari replika asli membutuhkan waktu sekitar 6 jam, menyiapkan "gambar dasar" membutuhkan waktu 7 jam, dan memperbarui basis data di lingkungan pengembang membutuhkan waktu 2 jam. Karena dua tindakan pertama dilakukan "di latar belakang" dan tidak terlihat oleh pengembang, sebenarnya mereka dapat menggunakan versi produksi dari basis data (dengan ukuran 1 TB) 
selama 2 jam yang sama .
Pertanyaan, kritik dan koreksi terhadap skema yang diusulkan dan komponennya disambut dalam komentar!
PS Tentu saja, kami memahami bahwa dalam kasus VMware dan beberapa alat lainnya, adalah mungkin untuk membuat snapshot dari mesin virtual dan meluncurkan virusalka baru dari snapshot (yang bahkan lebih cepat), tetapi opsi ini tidak termasuk menyiapkan pangkalan, dengan mempertimbangkan yang akan berubah hampir sama waktu ... Belum lagi fakta bahwa tidak semua orang memiliki kesempatan atau keinginan untuk menggunakan produk komersial.
PPS
Lainnya dari siklus tips & trik K8:
Baca juga di blog kami: