k8s 集羣中,rbd 和 cephfs 數據的備份腳本

cat >> k8s-ceph-bak.py <<EOF #!/usr/bin/env python #coding:utf-8 from kubernetes import client, config from executor import execute import datetime, sys, os, shutil import argparse import base64 import json if sys.version_info[0] < 3: import ConfigParser as configparser else: import configparser ceph_config_file = '/etc/ceph/ceph.conf' if os.getenv('SRC_CEPH_CONFIG_FILE') is None else os.getenv('SRC_CEPH_CONFIG_FILE') ceph_keyring = '/etc/ceph/ceph.client.admin.keyring' if os.getenv('SRC_CEPH_KEYRING') is None else os.getenv('SRC_CEPH_KEYRING') ceph_pool = 'kube' if os.getenv('CEPH_POOL') is None else os.getenv('CEPH_POOL') restic_data_dir = '/data/ceph/restic' backy2_data_dir = '/data/ceph/backy2' restic_password = 'enhjYXNkMTIz' def gen_backy2_conf(): if not os.path.exists(backy2_data_dir): os.makedirs(backy2_data_dir) if not os.path.isfile(backy2_data_dir + '/backy.sqlite'): with open('/etc/backy.cfg','w') as f: f.write(''' [DEFAULTS] logfile: /var/log/backy.log block_size: 4194304 hash_function: sha512 lock_dir: /run process_name: backy2 disallow_rm_when_younger_than_days: 6 [MetaBackend] type: backy2.meta_backends.sql engine: sqlite:///{backy2_data_dir}/backy.sqlite [DataBackend] type: backy2.data_backends.file path: {backy2_data_dir}/data simultaneous_writes: 5 simultaneous_reads: 5 [NBD] cachedir: /tmp [io_file] simultaneous_reads: 5 [io_rbd] ceph_conffile: /etc/ceph/ceph.conf simultaneous_reads: 10 new_image_features: RBD_FEATURE_LAYERING '''.format(backy2_data_dir=backy2_data_dir)) # 初始化backy2 倉庫 execute('backy2 initdb', capture=True, check=False, sudo=True) def kube_v1(): k8s_api = None if "KUBERNETES_SERVICE_HOST" in os.environ: config.load_incluster_config() k8s_api = client.CoreV1Api() else: try: config.load_kube_config() k8s_api = client.CoreV1Api() except FileNotFoundError: logger.info("No K8s") pass return k8s_api def get_all_pv(): k8s_api = kube_v1() all_pv = k8s_api.list_persistent_volume() return all_pv def get_rbd_pv_from_namespace(namespace): all_pv = get_all_pv() rbd_pv = [] for pv in all_pv.items: if pv.spec.claim_ref.namespace == namespace and pv.spec.storage_class_name == 'ceph-rbd': rbd_pv.append(pv) return rbd_pv def get_cephfs_pv_from_namespace(namespace): all_pv = get_all_pv() cephfs_pv = [] for pv in all_pv.items: if pv.spec.claim_ref.namespace == namespace and pv.spec.storage_class_name == 'cephfs': cephfs_pv.append(pv) return cephfs_pv # Create rbd image snapshot def create_rbd_image_snap(namespace): rbd_pv = get_rbd_pv_from_namespace(namespace) for pv in rbd_pv: rbd_pool = pv.spec.rbd.pool rbd_image_name = pv.spec.rbd.image snap_name = pv.spec.claim_ref.namespace + '-' + pv.spec.claim_ref.name + '-' + datetime.datetime.now().strftime('%Y%m%d%H%M%S') execute('rbd snap create {rbd_pool}/{rbd_image_name}@{snap_name}'.format(rbd_pool=rbd_pool, rbd_image_name=rbd_image_name, snap_name=snap_name), check=False, sudo=True) # from snapshot restore def restore_from_rbd_image_snap(namespace): rbd_pv = get_rbd_pv_from_namespace(namespace) for pv in rbd_pv: rbd_pool = pv.spec.rbd.pool rbd_image_name = pv.spec.rbd.image execute('rbd snap ls {rbd_pool}/{rbd_image_name}'.format(rbd_pool=rbd_pool, rbd_image_name=rbd_image_name), check=False, sudo=True) # backup rbd image that use backy2 def backup_rbd_image(namespace): rbd_pv = get_rbd_pv_from_namespace(namespace) for pv in rbd_pv: rbd_pool = pv.spec.rbd.pool rbd_image_name = pv.spec.rbd.image bak_rbd_image_name = pv.spec.claim_ref.namespace + '-' + pv.spec.claim_ref.name execute('backy2 backup -t {rbd_pool}/{rbd_image_name} rbd://{rbd_pool}/{rbd_image_name} {bak_rbd_image_name}'.format(rbd_pool=rbd_pool, rbd_image_name=rbd_image_name, bak_rbd_image_name=bak_rbd_image_name), check=False, sudo=True) execute('backy2 ls', check=False, sudo=True) def get_ceph_mon(ceph_conf_file): mon_str = execute('ceph mon_status', capture=True, check=False, sudo=True) mon_json = json.loads(mon_str) return mon_json['monmap']['mons'][0]['public_addr'].split(':')[0] def get_ceph_admin_secret(ceph_keyring): ceph_admin_secret = execute('ceph auth get-key client.admin', capture=True, check=False, sudo=True) return ceph_admin_secret # backup cephfs that use restic def backup_cephfs(namespace): cephfs_pv = get_cephfs_pv_from_namespace(namespace) bak_repo = '' for pv in cephfs_pv: # 備份的tag bak_tag = pv.spec.claim_ref.namespace + '-' + pv.spec.claim_ref.name + '-' + datetime.datetime.now().strftime('%Y%m%d%H%M%S') # 備份的repo,根據命名空間來區分 bak_repo = restic_data_dir + '/' + pv.spec.claim_ref.namespace # 數據存儲的具體路徑 cephfs_dir_path = pv.spec.cephfs.path cephfs_mountpoint = '/mycephfs' bak_path = cephfs_mountpoint + cephfs_dir_path if not os.path.exists(cephfs_mountpoint): os.makedirs(cephfs_mountpoint) # 獲取一個ceph 集羣的monitor 地址 ceph_mon_addr = get_ceph_mon(ceph_config_file) # 獲取訪問ceph 集羣的admin secret ceph_admin_secret = get_ceph_admin_secret(ceph_keyring) # 掛載cephfs文件系統 #if execute('mount.ceph {ceph_mon_addr}:/ {cephfs_mountpoint} -o name=admin,secret={ceph_admin_secret}'.format(ceph_mon_addr=ceph_mon_addr, cephfs_mountpoint=cephfs_mountpoint, ceph_admin_secret=ceph_admin_secret), check=False, sudo=True): if execute('ceph-fuse -m {ceph_mon_addr}:6789 {cephfs_mountpoint}'.format(ceph_mon_addr=ceph_mon_addr, cephfs_mountpoint=cephfs_mountpoint), check=False, sudo=True): # 備份前先創建一個軟連接,以方便查找 link_path = pv.spec.claim_ref.namespace + '-' + pv.spec.claim_ref.name os.chdir(cephfs_mountpoint) if os.path.exists(link_path): os.unlink(link_path) os.symlink(cephfs_dir_path.lstrip('/'), link_path) os.chdir('/') # 設置 restic 的repo 密碼 os.environ['RESTIC_PASSWORD'] = base64.b64decode(restic_password) # 初始化restic 的repo, 如果repo 不存在的話 if not os.path.exists(bak_repo): execute('restic init --repo {bak_repo}'.format(bak_repo=bak_repo), check=False, sudo=True) # 對指定命名空間的cephfs 形式的pvc 進行數據備份 execute('restic -r {bak_repo} backup --tag {bak_tag} {bak_path}'.format(bak_repo=bak_repo, bak_tag=bak_tag, bak_path=bak_path), check=False, sudo=True) # 卸載cephfs 文件系統 execute('umount {cephfs_mountpoint}'.format(cephfs_mountpoint=cephfs_mountpoint), check=False, sudo=True) # 查看備份的數據 if os.path.exists(bak_repo): execute('restic -r {bak_repo} snapshots'.format(bak_repo=bak_repo), check=False, sudo=True) # How many copies are kept up to date def keep_last_backup_counts(num=3): # for backy2 bak_diff_types = execute('backy2 -m ls | grep ^version | cut -d"|" -f3 | sort -u', capture=True, check=False, sudo=True).split('\n')[:-1] tail_num = num + 1 backy2_uuid_list = [] for bak_type_name in bak_diff_types: backy2_uuid_list.append(execute('backy2 -m ls |grep {bak_type_name} | sort -t"|" -k2r,2 | cut -d"|" -f7 | tail -n +{tail_num}'.format(bak_type_name=bak_type_name, tail_num=tail_num), capture=True, check=False, sudo=True)) for uuid in backy2_uuid_list: if uuid != '': execute('backy2 rm -f {uuid} '.format(uuid=uuid), check=False, sudo=True) execute('backy2 cleanup -f', check=False, sudo=True) execute('backy2 ls', check=False, sudo=True) # for restic os.environ['RESTIC_PASSWORD'] = base64.b64decode(restic_password) for ns in os.listdir(restic_data_dir): execute('restic -r {bak_repo} forget --keep-last={num} --prune'.format(bak_repo=restic_data_dir + '/' + ns, num=num), check=False, sudo=True) def parse_arguments(args=None): parser = argparse.ArgumentParser(description='Used to back up persistent data in the namespace specified in k8s') parser.add_argument('-a', '--all', dest='namespace', action="store", help='Specify the namespace in which persistent data needs to be backed up from cephfs and rbd image') parser.add_argument('-b', '--rbd', dest='rbd', action="store", help='Specifies the namespace in which persistent data needs to be backed up from rbd image') parser.add_argument('-f', '--cephfs', dest='cephfs', action="store", help='Specify the namespace in which persistent data needs to be backed up from cephfs') parser.add_argument('-c', '--counts', dest='counts', action="store", type=int, help='Specifies how many copies to keep up to date') parser.add_argument('-s', '--snapshot', dest='snapshot', action="store", help='Specifies the namespace in which persistent data needs to create snapshot') parser.add_argument('-r', '--restore-snap', dest='restoresnap', action="store", help='Restore rbd image from snapshot') try: args = parser.parse_args(args=args) except IOError as msg: parser.error(str(msg)) if len(sys.argv) == 1: parser.print_help(sys.stderr) sys.exit(1) return args def main(arg): args = parse_arguments(arg) if args.rbd: backup_rbd_image(args.rbd) elif args.counts: keep_last_backup_counts(args.counts) elif args.snapshot: create_rbd_image_snap(args.snapshot) elif args.cephfs: backup_cephfs(args.cephfs) elif args.namespace: backup_cephfs(args.namespace) backup_rbd_image(args.namespace) elif args.restoresnap: restore_from_rbd_image_snap(args.restoresnap) if __name__ == '__main__': gen_backy2_conf() main(sys.argv[1:]) EOF
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章