迁移Jellyfin Docker Volume 并打开硬件加速

之前为了方便迁移,Docker Compose部署的Application都尽量采用了 Volume 而不是 Binding的方式处理persistent storage。以为Portainer上面有UI可以方便的迁移Volume。谁知Portainer上面的Stack Migration并不会一并Copy Volume到新的主机。合着似乎只是把Docker compose的内容copy到新主机上面然后部署运行。不过既然用了Volume,那好歹也要用正确的方法迁移文件,而不是直接copy paste。于是就有了一篇日志。(Portainer太让人失望了。)

这里用的例子是 Jellyfin。因为之前在一台主机上面部署了GPU,于是想着先把Jellyfin硬解以及HDR的问题解决了。GPU直通详见:Proxmox 7.4 GPU Passthrough 显卡直通

首先,在原主机上Jellyfin的部署如下 (stack name: jellyfin):

version: "3"

volumes:
  jellyfin_config:
    name: jellyfin_config
  media:
    external: true

services:
  jellyfin:
    image: nyanmisaka/jellyfin:230305-amd64
    container_name: jellyfin
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/Vancouver
    volumes:
      - jellyfin_config:/config
      - media:/mnt/media
    ports:
      - 8096:8096
      # - 8920:8920 #optional, for https
      #- 7359:7359/udp #optional, local discovery
      #- 1900:1900/udp #optional, DLNA
    restart: unless-stopped
    tty: true

其中 Volume: Media 实际上是一个 Samba share,存实际的媒体文件。而volume: jellyfin_config是Jellyfin的配置文件以及各种metadata。这里需要迁移的就是这个jellyfin_config volume。这里 jellyfin_config 实际是给了一个特定的 name 的。这导致这个volume的全名就是 jellyfin_config 而不是 jellyfin_jellyfin_config (aka <stack_name>_<volumes_key_in_yaml>)。通常这个应该是需要在多个service或者app之间共享volume才需要的。不过这里已经是这样了,就不去纠结了。

sudo docker volume ls
# Should show "jellyfin_config" volume

sudo docker volume inspect jellyfin_config
# Details of the volume, including:
# * Mountpoint: the absolute path of the volume on the docker host machine:
#   * /var/lib/docker/volumes/jellyfin_config/_data
# * Labels: should contains the label: '"com.docker.compose.project": "jellyfin"'

单纯使用SCP迁移Volume可能遇到的问题是Permission可能会乱掉,新的文件的ownership以及ACL都有可能因为SCP而被破坏,这在目标机上面重新部署Docker container的时候可能造成很多麻烦。目前来讲比较干净又简单的方式是使用 rsync。但是因docker volume所在的位置一般都是 /var/lib/docker/volumes/,通常需要root权限,rsync 远程直接从Ubuntu上面搞还真不一定成。这个时候就会想念Debian上面可以打开SSH的Root用户。这里采用的方法是 busybox 。Docker官方的方法太过Heavy,[直接用Ubuntu image](https://docs.docker.com/storage/volumes/#back-up-a-volume),其实用busybox就足够了。

# 假设把backup.tar放在$HOME

sudo docker run --rm \
  -v jellyfin_config:/volume-backup-source \
  -v $HOME:/volume-backup-target \
  busybox sh -c \
  'cd /volume-backup-source && tar cf /volume-backup-target/backup.tar . '

# Result: A 'backup.tar' file in $HOME

之后SCP到目标机。目标机上面需要创建这个Volume。因为之前一直用的Portainer,所以这里依旧用Portainer来创建。也可以用 docker create volume <volume_full_name> 来创建。因为Jellyfin用了SAMBA share挂载Media文件,所以这里也需要创建相应的Media volume。

最后用 busybox container 再把 backup.tar 展开到目标 Volume 上面。

# Assume 'backup.tar' is at $HOME

sudo docker run \
  --rm \
  -v jellyfin_config:/volume-backup-target \
  -v $HOME:/volume-backup-source \
  busybox \
  sh -c 'cd /volume-backup-target && tar xf /volume-backup-source/backup.tar .'

在新的server上面,还有几个设置需要搞定,是关于Nvidia Docker的支持的。详见“关于Nvidia的Docker支持”章节

之后再在Jellyfin上面把 Jellyfin stack 跑起来就可以了。这次可以添加GPU加速了。(这里假定服务器是 Ubuntu,所以UID GID用的是1000。如果是Debian,一般默认的GID应该是100)