不小心删除了docker容器怎么恢复?

如果不小心执行了docker rm 容器A,通过查找到docker volumes中删除容器的数据,将其挂载到新启动的容器中,即可恢复应用+状态+数据。

背景

docker作为优秀的开源容器引擎,能够像集装箱一样以一种非常标准化、轻量、可移植的方式帮助我们进行开发、交付和运行应用程序。

当镜像运行起来成为容器时会需要相关的存储资源来支撑软件服务的运行,如存储的日志文件,用户传入的数据,伴随运行生成的文件等。

由于docker运行程序实在是太便利了,在有些时候,一些用于调试的docker项目(自研、开源),不知不觉就投入了使用,并没有对数据做很合理安全的配置(挂载,备份),此后如果一不小心删除了运行中的容器,再启动镜像时,就会发现这是一个全新的服务,之前程序运行过程中积累的状态数据、用户数据、日志数据不复存在。

最近就遇到了这个问题:之前调研了一个开源的OA系统,在测试阶段,很好用,对接了内部的认证系统后,很多同事纷纷上去试用,不知不觉积累了很多数据。

直到有一天,运维同事在这个开发环境为了升级相关服务,便执行了如下容器删除语句:

1
docker rm {这个OA容器}

当天晚上,很多行政同事就炸锅了,反馈以为是正式环境,自己这一周的相关报表、报告都在上面。

怎么办?

关于Volumes机制

如下图,可以看到docker容器运行过程中,相关的应用数据可以分为三类。

1 bind mount

类似linux,可以将宿主机上的文件系统的某个路径、地址挂载到容器内的某个路径下,实现数据的持久化。比如将宿主机的/data/nfs/log/appA/挂载到容器内的/var/log/,即可在容器外部的/data/nfs/log/下轻松获取到各个应用(如appA)的日志信息。

2 volume

使用docker自带的volume机制进行数据的持久化。docker会在宿主机的特定位置(如/var/lib/docker/volumes)维护各个volumes,并提供了一系列的维护CLI命令。在启动容器时,类似mount一样,可以将某个volumes挂载到容器内的指定目录。docker自带的volume相比使用宿主机的文件系统存储有若干的好处,详见官方文档

3 tmpfs mount

如果在mount时使用了tmpfs(temporary file system, 临时文件存储),docker容器运行时的相关数据不会进行持久化存储:容器如果被删除了,那么相关存储也会被清除掉。

综上,面对容器删除的问题,有如下几个结论。

  1. 容器启动(docker run)时,即使没有显示的指定-v -mount参数,容器的相关数据已经被作为volumes持久化存储了,比如在/var/lib/docker/volumes下面,所以数据是在的。
  2. 但如果 docker run时,显示指定使用了tmpfs的mount类型,删除容器时才会被清理。
  3. 或者如果docker rm时指显示定了-v参数,也会连带删除容器依赖的volumes数据。

所以,只要没有手残强制使用了临时存储或者删除时强制指定了删除数据,数据还是在的。

如何恢复

知道了volumes的存在,那么恢复的路径也就随之明确了。

  1. 找到删除容器对应的volumes
  2. 基于一样的镜像启动新的容器
  3. 将之前的volumes迁移/挂载到新的容器上

1 找到删除容器的volumes

如下图,进入volumes存储目录(比如CentOS在/var/lib/docker/volumes), 通过ls或者du等命令来观察目录的创建时间和大小,来推断删除容器的volumes是哪(几)个。

如果通过时间,大小等维度不容易判断,就需要根据文件夹内部的文件内容、目录结构来推断。比如可以启动一个新的容器,看生成的volumes内部的目录结构来对之前的目录进行判断。

注: 找到volume后先进行备份

2 启动新的容器

基于之前的镜像,直接启动容器,通过inspect命令查看新容器的volume名称。

如下图,通过docker inspect 新容器可以看到在Mounts列表中,type为volume的就是我们要找的目标,从Source字段即可定位到目录位置。

注: 如果对于镜像的使用比较了解了,则可以直接在启动时,将找到的volumes挂载到正确的位置即可略过第3步骤:将之前的volumes迁移/挂载到新的容器上

3 将之前的volume迁移/挂载到新的容器上

  1. 执行docker stop 新容器停止新容器。
  2. 使用mv命令,在/var/lib/docker/volumes中将步骤1获取的目录的名称修改为步骤2获取的新容器的目录名称:mv 旧的volume名称 新的volume名称
  3. 执行docker start 新容器再次启动新容器。
  4. 完成

延伸

k8s中,volume的路径在/var/lib/kubelet/pods/, 通过在node上对指定pod对应的容器执行docker inspect可以看到对应的容器数据存储路径。

结论

  1. 如果不小心执行了docker rm 容器A,是可以恢复应用+状态+数据的。
  2. 去/var/lib/docker/volumes下,能够看到一系列的volumes,根据目录里面的内容、创建时间、更新时间判断哪(几)个目录是属于你删除的容器A的。
  3. 先备份好这些volumes。
  4. 基于相同的镜像新启动一个容器B(虽然没有数据)。
  5. 通过docker inspect 容器B找到容器B在/var/lib/docker/volumes中的volume目录。
  6. 执行docker stop 容器B停止容器B。
  7. 使用mv命令,在/var/lib/docker/volumes中将容器A目录的名称修改为容器B目录的名称。
  8. 执行docker start 容器B再次启动容器B。
  9. 顺利的话,你之前删除的容器应用+应用状态+应用数据应该都恢复了。

参考资料

https://docs.docker.com/storage/volumes/