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
/dump
ke 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.txt
flag /dump/version.txt
akan 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: