How to transfer data between Kubernetes Persistent Volumes (quick and dirty hack)

I've recently been using fluxv2's helm controller to deploy applications into multiple clusters. Β In one instance I was replacing a manually-created 2Gb PVC for a postgres database with a new one created with a statefulset deployed by helm. The original PVC had been created outside of the helm chart values.yaml using existingClaim, and so didn't have the correct name, and wasn't "adopted" by the helm release when I upgraded it (see timestamps below)

I didn't want to have to wipe the database and start from scratch though, since that would represent hours of rework, so I came up with this quick-and-dirty hack:

First, I deleted all the daemonsets and statefulsets in the namespace, leaving the PVCs remaining (but not being accessed):

root@cowboy:~# kubectl delete deployments.apps -n harbor --all
deployment.apps "harbor-chartmuseum" deleted
deployment.apps "harbor-core" deleted
deployment.apps "harbor-jobservice" deleted
deployment.apps "harbor-nginx" deleted
deployment.apps "harbor-notary-server" deleted
deployment.apps "harbor-notary-signer" deleted
deployment.apps "harbor-portal" deleted
deployment.apps "harbor-registry" deleted
root@cowboy:~# kubectl delete statefulsets.apps -n harbor --all
statefulset.apps "harbor-postgresql" deleted
statefulset.apps "harbor-redis-master" deleted
statefulset.apps "harbor-trivy" deleted
root@cowboy:~# kubectl get pvc -n harbor
NAME                     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      AGE
data-harbor-postgresql   Bound    pvc-48ffeb85-4dba-4c3c-bc46-545dc49c87dd   2Gi        RWO            rook-ceph-block   75d
data-harbor-trivy-0      Bound    pvc-7e17a7ec-c9fb-4095-9144-cc1e3ca5ceb2   5Gi        RWO            rook-ceph-block   75d
harbor-chartmuseum       Bound    pvc-9405407e-9783-4494-984f-66f1ca7cd593   5Gi        RWO            rook-ceph-block   75d
harbor-jobservice        Bound    pvc-ae39ff46-c2d2-45b8-89d0-07d9061e86ff   1Gi        RWO            rook-ceph-block   75d
harbor-registry          Bound    pvc-d6c5694c-9723-43f6-b0f7-522bde19823c   100Gi      RWO            rook-ceph-block   75d
root@cowboy:~# kubectl get pods -n harbor
No resources found in harbor namespace.
root@cowboy:~#

Then I created a template pod YAML using kubectl run -n harbor datamigrator --image=alpine Β -o yaml --dry-run=client > /tmp/datamigrator.yaml. Here's what it looked like initially:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: datamigrator
  name: datamigrator
spec:
  containers:
  - image: alpine
    name: datamigrator
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

Then I edited my new /tmp/datamigrator.yaml to include two PVCs, and updated the command/argument to sleep for an hour. Now it looks like this:

root@cowboy:~# cat /tmp/datamigrator.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: datamigrator
  name: datamigrator
spec:
  volumes:
  - name: old
    persistentVolumeClaim:
      claimName: data-harbor-postgresql
  - name: new
    persistentVolumeClaim:
      claimName: data-harbor-postgresql-0
  containers:
  - image: alpine
    name: datamigrator
    resources: {}
    command: [ "/bin/sleep" ]
    args: [ "1h" ]
    volumeMounts:
    - name: old
      mountPath: /old
    - name: new
      mountPath: /new
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

I deployed the pod by running kubectl apply -f /tmp/datamigrator.yaml Β -n harbor, and then exec'd into the freshly-created pod by running kubectl exec -n harbor datamigrator -it /bin/ash.

Finally, I wiped out the contents of /new, and moved (in hindsight, it would have been better to copy, to be safe) all the data from /old to /new:

root@cowboy:~# kubectl exec -n harbor datamigrator -it /bin/ash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead.
/ # df
Filesystem           1K-blocks      Used Available Use% Mounted on
overlay               51474912  38422156  10414932  79% /
tmpfs                    65536         0     65536   0% /dev
tmpfs                131935904         0 131935904   0% /sys/fs/cgroup
/dev/rbd2              1998672    337928   1644360  17% /old
/dev/rbd1              8191416    104400   8070632   1% /new
/dev/mapper/cow0-var 933300992   5867160 927433832   1% /etc/hosts
/dev/mapper/cow0-var 933300992   5867160 927433832   1% /dev/termination-log
/dev/mapper/VG--nvme-containerd
                      51474912  38422156  10414932  79% /etc/hostname
/dev/mapper/VG--nvme-containerd
                      51474912  38422156  10414932  79% /etc/resolv.conf
shm                      65536         0     65536   0% /dev/shm
tmpfs                131935904        12 131935892   0% /run/secrets/kubernetes.io/serviceaccount
tmpfs                131935904         0 131935904   0% /proc/acpi
tmpfs                    65536         0     65536   0% /proc/kcore
tmpfs                    65536         0     65536   0% /proc/keys
tmpfs                    65536         0     65536   0% /proc/timer_list
tmpfs                    65536         0     65536   0% /proc/sched_debug
tmpfs                131935904         0 131935904   0% /proc/scsi
tmpfs                131935904         0 131935904   0% /sys/firmware
/ # ls /old
conf        data        lost+found
/ # ls /new
conf        data        lost+found
/ # mv /^C
/ # ls /new
conf        data        lost+found
/ # mv /old/* /new/
mv: can't remove '/new/conf': Is a directory
mv: can't remove '/new/data': Is a directory
mv: can't remove '/new/lost+found': Is a directory
/ # rm -rf /new/*
/ # mv /old/* /new/
/ # exit
root@cowboy:~#

Having finished with the datamigrator pod, I deleted it, and re-deployed my helm chart. The postgres data is found in the new PVC, and I deleted the old (and now empty) PVC.