docker卷备份中缺少符号链接

时间:2018-08-10 10:17:22

标签: docker

我正在尝试使用docker文档中的方法备份/还原docker卷:https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes

步骤是1)创建一个新容器并安装所需的卷以进行备份2)将其压缩并保存在主机上。

这按预期工作,但是卷内的符号链接已删除(我要备份seafile:https://github.com/haiwen/seafile-docker)。我已经读过符号链接在绑定时已解决,并且我认为无效链接会自动删除,但我不确定到底会发生什么。符号链接只是消失了。

有人有同样的问题吗?我在互联网上找不到很多东西,而且奇怪的是原始docker-doc中的方法不起作用,所以我认为这是我的问题。

作为一个旁注,我使用docker-py制作了一个python脚本进行备份/还原,这可能对某人有用:

#!/usr/bin/env python3

"""
This module can backup and restore docker volumes from and to a tar.bz2 format.
The files are named by YearMonthDay_HourMinuteSecond e.g. backup_20180808_201956.tar.bz2

:Example usage:
    .. code-block:: python

      import volumes
      volumes.backup()
      volumes.restore('backup_20180808_201956.tar.bz2', override_existing_volumes=True):
"""

import docker
import datetime
import io
import tarfile
import os.path


def backup(save_path, volumes=[]):
    """
    This function is used to backup volumes. It saves a tar file containing the specified volumes to the desired path.
    By default, all volumes will be backed up.

    :param save_path: path where the tar file should be saved to e.g. /home/user/Downloads
    :type save_path: str
    :param volumes: by default, all volumes will be backed up, but a list can be speficied if needed e.g. ['test_vol']
    :type volumes: list of strings
    :return: None
    """

    client = docker.from_env()

    # by default, take all volumes available
    if not volumes:
        for volume in client.volumes.list():
            volumes.append(volume.attrs['Name'])

    # name of the backup file with automatic timestamp
    backup_folder_name = 'backup_%s' % datetime.datetime.now().strftime("%Y%m%d_%H%M%S")

    # temporary docker container is needed for backup, delete previous one if available
    try:
        container = client.containers.get('volume_backup_automation')
        container.stop()
        container.remove()
    except Exception:
        pass

    # create mounting points for all volumes
    volume_mounts = []
    for volume in volumes:
        volume_mounts.append(docker.types.services.Mount(target='/%s/%s' % (backup_folder_name, volume), source=volume))

    # create temporary docker container for mounting volumes and backup
    container = client.containers.run('ubuntu', name='volume_backup_automation', detach=True,
                                      volumes={save_path: {'bind': '/host_mount', 'mode': 'rw'}},
                                      mounts=volume_mounts, tty=True)

    # tar mounted volumes (mounted to /backup_folder_name/volumes) to host mount (save to/host_mount/backup_folder_name)
    container.exec_run(['tar', '-cjf', 'host_mount/%s.tar.bz2' % backup_folder_name,
                        '%s/' % backup_folder_name])[1].decode('UTF-8')

    # some useful commands
    # print('Host Files: \n %s' % container.exec_run(['ls', '/host_mount'])[1].decode('UTF-8'))
    # print('Client Files: \n %s' % container.exec_run(['ls', '/%s' % backup_folder_name])[1].decode('UTF-8'))

    # stop and remove temporary container
    container.stop()
    container.remove()
    volumes.clear()

    if not os.path.isfile(save_path + '/' + backup_folder_name + '.tar.bz2'):
        raise Exception('backup failed!')


def restore(restore_file_path, override_existing_volumes=False):
    """
    This function restores volumes from a tar file created by the backup function.

    :param restore_file_path: path to the tar file which will be restored e.g. '/home/backup_20180609_225855.tar.bz2'
    :type restore_file_path: str
    :param override_existing_volumes: if True, volumes with the same same as in the tar file will be overridden
    :type override_existing_volumes: bool
    :return: None
    """

    client = docker.from_env()

    # open tar archive
    archive = tarfile.open(restore_file_path, mode='r')

    # extract volume names from archive (e.g. backup_20180603_140642/volume_1)
    volume_names = []
    for archive_folder_names in archive.getnames():
        # loop through all folders inside tar file
        if len(archive_folder_names.split('/')) == 2:
            # only take second level folder names as volume names
            volume_name = archive_folder_names.split('/')[1]
            volume_names.append(volume_name)
        elif len(archive_folder_names.split('/')) == 1:
            backup_name = archive_folder_names

    # volumes with the same name should not be overwritten, check if they already exist and remove from list if they do
    if not override_existing_volumes:
        for volume_name in list(volume_names):
            try:
                # if volume with same name exist, delete name from volume names to avoid overwriting the volume
                volume_name = client.volumes.get(volume_name).attrs['Name']
                volume_names.remove(volume_name)
                print('Volume %s alredy exists! The Volume will not be overwritten!' % volume_name)
            except Exception:
                pass
    print('Volumes to be restored: %s' % volume_names)

    # temporary docker container is needed for restore, delete previous one if available
    try:
        container = client.containers.get('volume_restore_automation')
        container.stop()
        container.remove()
    except Exception:
        pass

    # create mounting point for volumes
    mounting_points = []
    for volume_name in volume_names:
        mounting_points.append(docker.types.services.Mount(target='/%s' % volume_name, source=volume_name))

    # start temporary docker container and mount volumes, this will create a volume automatically if not existent
    container = client.containers.create('ubuntu', name='volume_restore_automation', detach=True,
                                         mounts=mounting_points, tty=True)
    container.start()

    # copy archive to tmp folder in container (automatic extraction)
    with open(restore_file_path, 'rb') as file:
        bytestream = io.BytesIO(file.read())
        container.put_archive(path='/tmp', data=bytestream)

    # copy all files from archive to mounting point
    for volume_name in volume_names:
        # remove everything in volume
        container.exec_run(['rm -R', '/%s/*' % volume_name])
        # copy files to volume
        container.exec_run(['cp', '-rp', '/tmp/%s/%s/.' % (backup_name, volume_name), '/%s' % volume_name])

    # some useful commands
    # print(container.exec_run(['ls', '/'])[1].decode('UTF-8'))
    # print('Host Files: \n %s' % container.exec_run(['ls', '/tmp/%s' % backup_name])[1].decode('UTF-8'))

    # stop and remove temporary container
    container.stop()
    container.remove()

已解决:该问题实际上与符号链接没有直接关系,但是由于所有权问题(cp -r与cp -rp)而产生了副作用。我已经相应地调整了代码,它现在应该可以工作,但是符号链接在docker中仍然很痛苦。

0 个答案:

没有答案