如何对未数据持久化的现存容器进行数据持久化
问题发现
在刚开始研究和学习docker容器化技术的时候,一些应用和服务的容器化部署,无论是通过docker run
命令还是docker compose
编排,都没有统一,都是跟着部署样例进行操作,导致有些容器在部署的时候没有进行数据持久化。
然后我发现服务器上有的时候需要备份、或者数据转移、或者是容器需要更新到最新版本镜像的时候,数据没做持久化,就会很困扰。
容器数据持久化概念
容器持久化是指确保容器在停止、删除或重建后,其内部生成或修改的关键数据能够保留下来。默认情况下,容器的文件系统是临时的(其可写层的数据会随容器生命周期而消失)。
核心原理在于将数据存储与容器本身解耦。我们不将重要数据写入容器自身的可写层,而是通过挂载(Mapping)的方式,将宿主机上的外部存储空间映射到容器内部的指定路径。
主要实现方式有两种:
数据卷 (Volumes): 这是Docker推荐且管理的数据存储区域,生命周期独立于容器,提供更可靠的数据管理。
绑定挂载 (Bind Mounts): 直接将宿主机上的任意目录或文件挂载到容器内,简单灵活,但宿主机路径需自行管理。
解决思路
1.从当前正在运行的容器中,把配置数据复制到宿主机上。
2.停止并移除旧容器。
3.使用新版镜像,并通过挂载宿主机的目录(就是你刚才复制出来的配置数据存放的目录)到容器内部,从而创建新的容器。
实操案例:Sun-Panel容器
背景:我的容器最初通过docker run命令进行容器部署,但是没有对数据进行挂载(也就是数据持久化),这样每当容器被删除或者重建的时候,数据就会全部丢失(比如前段时间的1panel系统版本从V1升级V2升级,我需要对整个服务器的容器进行备份的时候,我就发现了问题)
方法1(终端copy命令):
步骤1:进入容器、找到配置文件和数据文件的存放路径(参考Sun-panel的文档说明),应该是容器内的:/app/conf
文件夹
#从服务器终端,通过以下命令进入容器内部
docker exec -it <你的sun-panel容器ID或名称> sh # 或者 bash,如果sh不行
#进入容器后,查找配置文件和数据文件通常存在的目录。(这里我查了文档知道容器内路径,也可以通过搜索文件关键字来搜索)
ls -l /app/conf
find / -name "*config*" -type d #包含config名称的文件
find / -name "*.db" #后缀是.db的文件
find / -name "*.sqlite*" #后缀是.sqlite的文件
#在容器内找到对应的配置和数据文件目录后,退出容器
exit
步骤2:将容器内的数据复制到宿主机
#在宿主机上创建一个用于存放 Sun-Panel 数据的目录。
mkdir -p /opt/sun-panel/data
#获取你当前 sun-panel 容器的 ID 或名称。
docker ps #找到 sun-panel 容器,记录下 CONTAINER ID 或 NAMES 列的名称
#将容器内部的数据复制到宿主机创建的目录。
docker cp <你的sun-panel容器ID或名称>:/app/conf /opt/sun-panel/data/
#这里的 /app/data 是容器内的路径,而 /opt/sun-panel/data/ 是宿主机上的路径。
#检查复制的数据是否都在宿主机的新目录中。
ls -l /opt/sun-panel/data
第二步我的服务器因为未知原因(提示找不到容器中的目录),所以我采取第二种数据复制方法
步骤3:停止并删除旧容器
#停止旧的 sun-panel 容器。
docker stop <你的sun-panel容器ID或名称>
#删除旧的 sun-panel 容器。
docker rm <你的sun-panel容器ID或名称>
步骤4:拉取最新的镜像并且创建新容器,设置好数据持久化挂载volume
#拉取最新版本的 sun-panel 镜像
docker pull HCLonely/sun-panel:latest
#使用数据卷挂载启动新容器,端口自己设置,-v是挂载路径(格式:宿主机路径:容器内路径)
docker run -d \
--name sun-panel \
-p <你的宿主机端口>:80 \
-v /opt/sun-panel/data:/app/data \
--restart unless-stopped \
hslr/sun-panel:latest
#检查新容器的状态和日志
docker ps -a
docker logs sun-panel
方法2(容器内压缩、移动、宿主机解压缩):
步骤1:
进入容器、找到配置文件和数据文件的存放路径(参考Sun-panel的文档说明),结果找到数据文件在:/app/conf/database/database.db
为了安全起见,我们最好把整个/app/conf
目录都持久化出来,因为除了数据库文件,可能还有其他配置文件(即使你没改过,程序内部也可能有默认配置在这个目录里)。
步骤2:将容器内的数据和配置文件的目录打包
#重新进入 sun-panel 容器的终端
docker exec -it f7fa45a68f96 /bin/sh
# 如果你的容器默认shell是bash,也可以用 /bin/bash
# 进入 /app 目录,这样打包时目录结构保持为 `./conf`
cd /app
# 打包 conf 目录,并保存到 /tmp/conf_backup.tar
tar -cvf /tmp/conf_backup.tar conf
#tar -cvf: c 创建归档,v 显示详细信息,f 指定文件名。
#conf: 这里是相对路径,表示当前目录下的 conf 目录。
#确认 tarball 文件已创建成功
ls -l /tmp/conf_backup.tar
#退出容器终端
exit
步骤3:把打包文件转移到宿主机并且解压,检查无误后,清理压缩包。
#在宿主机上创建用于存放 Sun-Panel 数据的根目录
mkdir -p /opt/sun-panel/
#将容器内部的 conf_backup.tar 文件复制到宿主机创建的目录
docker cp 容器id:/tmp/conf_backup.tar /opt/sun-panel/
#在宿主机上解压 tarball 文件
cd /opt/sun-panel/
tar -xvf conf_backup.tar
#验证解压后的目录结构和内容
ls -l /opt/sun-panel/conf/
ls -l /opt/sun-panel/conf/database/
#清理:删除宿主机上的 tarball 文件
rm /opt/sun-panel/conf_backup.tar
于我们打包时是从 /app
目录进行的 tar -cvf /tmp/conf_backup.tar conf
,所以解压后你会得到一个 /opt/sun-panel/conf/
目录。
步骤4:停止删除旧容器、拉取最新镜像构建新容器(带数据挂载),在/sun-panel
目录下创建docker-compose.yaml
#停止旧的 sun-panel 容器
docker stop 容器名称或者容器id
#删除旧的 sun-panel 容器
docker rm 容器名称或者容器id
#拉取最新版本的 sun-panel 镜像
docker pull hslr/sun-panel:latest
#然后用右侧compose编排文件创建容器
docker compose up -d
version: "3.2"
services:
sun-panel:
image: "hslr/sun-panel:latest"
container_name: sun-panel
volumes:
- ./conf:/app/conf # 关键:将宿主机当前目录下的 conf 目录映射到容器的 /app/conf
ports:
- 3002:3002 # 端口映射,宿主机端口3002 -> 容器端口3002
restart: always
networks:
- ABCD #如果不额外写网络,则会自动创建一个新的桥接网络,这里我们沿用之前已经创建好的桥接网络ABCD
networks:
ABCD:
driver: bridge
external: true
最终大功告成。
- 感谢你赐予我前进的力量