我正在尝试使用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中仍然很痛苦。