文章目录
容器数据存储简介
默认情况下,在运行中的容器里创建的文件,被保存在一个可写的容器层:
- 如果容器被删除了,那么容器里面的数据也没有了。
- 这个可写的容器层和特定的容器层绑定的,也就是当前容器的数据和其他容器不能共享。
如果希望自己的容器数据保留下来(持久化),则需要将数据存储在数据卷上。数据卷和容器是解耦的,从而可以独立创建并管理数据卷,即使关联容器被删除了,数据卷不会被删除。
docker主要提供了两种方式做数据的持久化
- Data Volume(数据卷),由docker管理,它存在宿主机上的,linux是
/var/lib/docker/volumes
目录下,windows是在C:\ProgramData\Docker\windowsfilter
下,它是持久化数据最好的方式。 - Bind Mount(磁盘)由用户指定存储在宿主机的具体目录上。
容器和镜像的底层关系
- 容器其实是一个运行中的镜像
- 实质是复制image并在image的最上面加一层
可读可写
的层,(称之为container layer
,容器层) - 基于同一个image可以创建多个container
- 如果没有设置数据卷,那么默认数据是保存在container layer中,如果容器删除这其中的数据也跟着删除了,这就是为什么数据要做持久化的原因。
数据持久化(Data Volume方式)
这是采用数据卷的方式,也是系统推荐的。在使用的时候,即可以先创建数据卷,也可以直接启动容器,容器会自动创建数据卷。同时支持配置多数据卷。
-
先创建数据卷方式:
docker volume create [volume_name] -
启动容器时创建:
-v [数据卷名称]:[容器中的目录]
# 以启动redis容器为例
docker run -itd --name=redis -v redis-data:/data -v redis-conf:/etc/redis -p 6379:6379 -e LANG=C.UTF-8 redis:latest
- 查看数据卷的列表
docker volume ls
- 查看数据卷的详情
docker volume inspect redis-data
docker volume inspect redis-data
数据持久化(Bind Mount方式)
格式:-v [宿主机上的目录]:[容器目录]
注意:容器运行时需要可读可写目录权限,—privileged=true
这是我们需要在宿主机上创建目录,然后将目录和容器的目录进行绑定就行,同时支持配置多数据卷
假如安装mongodb时,就在宿主机目录下分别创建数据目录/data/docker-mongo/data
和 配置文件目录/data/docker-mongo/config
,启动容器时可以这样子来操作:
docker run -it --name=mongo -v /data/docker-mongo/data:/data/db -v /data/docker-mongo/config:/data/configdb --restart=always --privileged=true -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=123456 -d mongo --auth
查看数据卷是否挂载成功:
docker inspect mongo
数据卷容器
数据卷容器是一个专门用来挂载数据卷的容器,该容器作用主要是来共享数据的,即供其他容器使用。所谓的数据卷容器,实际上就是一个普通的容器,它提前建立了和宿主机的目录映射,其他容器来引用它。
使用场景
- 方便容器数据来做备份,恢复,迁移。
- 如果你有一些持续更新的数据需要在容器之间共享,最好创建数据卷容器。
- 多个容器挂载同一个数据卷(缺点:操作比较麻烦,因为当容器过多时,需要一个个的挂载。
特性
数据卷中的数据并不能继承于镜像,也不在联合文件系统临时层所管理的范围内,所以镜像层面的写时复制不会作用于数据卷中的数据,而这些数据也不会被docker commit
提交到新的镜像中。
- 文件的操作不是在沙盒环境中进行的,而是直接作用于宿主机内真实的硬盘I/O中;
- 生命周期不受容器控制(即使数据容器删除了,数据卷也还在),能够安全有效的存储文件到数据卷中;
- 数据卷独立于容器之外,可以实现多个容器共享一个数据卷。
如下图所示:
创建数据卷的姿势
docker run --volumes-from [容器名称] -d [镜像名称]
举例1共享nginx容器
1. 创建nginx数据容器卷:
以ubuntu为镜像的数据镜像,并在容器里面创建/usr/share/nginx/html的目录,作为后面nginx容器 共享目录。
docker run -itd -v /usr/share/nginx/html/ --name mydata ubuntu:latest
在Portainer的数据卷管理里面可以看到数据卷容器,就存在宿主机的/var/lib/docker/volumes
中
2. 引用容器
此时再创建两个nginx容器,nginx1和nginx2,同时数据卷引用mydata
容器。
docker run -itd --volumes-from mydata -p 80:80 --name=nginx1 --restart=always --privileged=true nginx:latest
docker run -itd --volumes-from mydata -p 81:80 --name=nginx2 --restart=always --privileged=true nginx:latest
这时三个容器内,都有了usr/share/nginx/html
目录。任意一个修改了该目录下的文件,其他两个容器都会同步。通过查看nginx1,nginx2的容器详情就会发现其数据卷挂载的位置都是相同的,都是挂载在/usr/lib/docker/volumes/.../_data
目录下。
3. 删除数据容器的影响
下面测试是删除数据容器mydata后,两个nginx容器是否受到影响
docker container stop mydata
docker container rm mydata
如下图,从portainer的数据卷明细中看到,nginx1,nginx2的容器都还是绑定原来的目录,并没有被删除。说明数据容器对应的/usr/share/nginx/html目录绑定的是在/var/lib/volumes,所以mydata数据容器删除,并不影响数据卷。
举例2忘记mongodb的密码
通过将原来的容器作为数据容器,以无免密启动临时容器,来修改密码
- 停止原有容器
docker container stop mongo
- 启动临时容器,将原有容器作为数据卷,并免密登录
# 参数--rm表示临时容器,等容器停止后自动删除
docker run -it --name mongo-repair --rm --volumes-from mongo mongo:6.0.8 --noauth
- 进入新容器,修改密码
# 另开一个终端
docker exec -it mongo-repair mongosh --port 27017
# 进入admin数据库
use admin
db.updateUser("admin",{pwd:"123456"})
# 退出数据库
exit
- 停止新容器
# 停止容器后自动删除
docker container stop mongo-repair
- 启动原来容器
docker container start mongo
数据的备份和恢复
利用数据卷容器可以实现数据的备份和恢复。
备份
- 格式:
docker run --volumes-from [待备份容器] -v $(pwd):/backup --name=[临时容器名] [镜像] tar -cvf /backup/backup.tar [容器数据卷]- 命令解释:
- 首先使用–volume-from连接 待备份容器。
- 创建临时容器,来作为备份的数据容器
- -v参数用来将宿主机当前目录挂载到容器的/backup目录下。
- 接下来将容器中的
/usr/share/nginx/html
目录下的的内容,使用tar命令压缩生成到/backup/backup.tar
文件中去,由于已经设置将宿主机当前目录映射到容器的/backup目录,所以容器/backup下的tar文件在当前目录能马上看到
例子,以容器nginx1为例子
docker run --volumes-from nginx1 -v $(pwd):/backup --name=nginx1-copy nginx:latest \
tar -cvf /backup/backup.tar /usr/share/nginx/html
恢复
数据恢复前先准备一个存放恢复数据的容器,需要挂载一个数据卷,这样子方便给外部容器连接
docker run -itd -P -v /usr/share/nginx/html/ --name nginx3 nginx:latest
再准备一个临时容器,用来恢复数据用
恢复命令格式:docker run --volumes-from [新建的容器] -v 存放备份目录:/当前容器数据目录 [镜像] tar xvf [备份的压缩包路径]
docker run --volumes-from nginx3 -v $(pwd):/backup nginx tar xvf /backup/backup.tar
1, 首先还是使用–volumes-from参数连接上备份容器,即第一步创建出来的nginx3
2. 然后将当前目录映射到容器的/backup目录下
3. 然后执行解压操作,将backup.tar文件解压。解压文件位置描述是一个容器内的地址,但是该地址已经映射到宿主机中的当前目录了,因此这里要解压缩的文件实际上就是宿主机当前目录下的文件。
多主机之间的容器数据共享
docker的数据卷支持多种driver,默认都是和宿主机绑定在一起,也就是在本地的。
$ docker volume inspect redis-data
[
{
"CreatedAt": "2023-09-04T17:24:08+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/redis-data/_data",
"Name": "redis-data",
"Options": null,
"Scope": "local"
}
]
如果想要多个远程主机实现数据共享,我们需要用到sshfs
驱动。以下是我在mac上安装3个ubuntu虚拟机,来进行测试。有了OrbStack
,安装虚拟机也就很方便。
下面是使用远程文件共享的架构图
环境准备
远程主机的ssh服务和端口需要开启
准备在orbstack
安装好的3台ubuntu虚拟机,在虚拟机里已经装好了docker环境,我们通过sshfs将ubuntu1和ubuntu2的数据卷关联到ubuntu3上宿主目录上。
hostname | ip | ssh_user | ssh_password |
---|---|---|---|
ubuntu1 | 198.19.249.150 | allen.huang | 123456 |
ubuntu2 | 198.19.249.19 | allen.huang | 123456 |
ubuntu3 | 198.19.249.171 | allen.huang | 123456 |
安装sshfs插件
在ubuntu1和ubuntu2上都安装
docker plugin install --grant-all-permissions vieux/sshfs
查看插件
docker plugin ls
ID NAME DESCRIPTION ENABLED
ca79b48ec4d0 vieux/sshfs:latest sshFS plugin for Docker true
通过sshfs创建数据卷
格式:
docker volume create --driver vieux/sshfs
-o sshcmd=[用户名]@远程机器IP:远程机器的目录
-o password=[密码] [数据卷名称]
示例:
在ubuntu1上创建数据卷,以下以非root账号运行,所以带上sudo
sudo docker volume create --driver vieux/sshfs -o sshcmd=allen.huang@198.19.249.171:/home/allen.huang -o password=123456 ssh_volume1
查看数据卷列表
$ sudo docker volume ls
DRIVER VOLUME NAME
vieux/sshfs:latest ssh_volume1
查看数据卷详情
$ sudo docker inspect ssh_volume1
[
{
"CreatedAt": "0001-01-01T00:00:00Z",
"Driver": "vieux/sshfs:latest",
"Labels": null,
"Mountpoint": "/mnt/volumes/bd8ecd8c4fc7cea8ca46542864096b18",
"Name": "ssh_volume1",
"Options": {
"password": "123456",
"sshcmd": "allen.huang@198.19.249.171:/home/allen.huang"
},
"Scope": "local"
}
]
创建容器挂载volume
创建容器,挂载ssh_volume1到/data目录,然后进入容器的shell,在/data中创建一个text.txt文件,写入一些内容
allen.huang@ubuntu1:~$ sudo docker run -it --name=busybox -v ssh_volume1:/data busybox sh
/ # LS
sh: LS: not found
/ # ls
bin data dev etc home lib lib64 proc root sys tmp usr var
/ # cd data
/data # ls
/data # echo 'test ssh volume' > test.txt
/data # cat test.txt
test ssh volume
/data #
/data #
在查看ubuntu3查看文件test.txt文件也已经存在,这说明已经同步成功
同时在ubuntu2上像ubuntu1上的方式创建数据卷,并创建容器挂载数据
sudo docker run -it --name=busybox -v ssh_volume:/data busybox sh
/ # ls
bin data dev etc home lib lib64 proc root sys tmp usr var
/ # cd data
/data # echo 'test2' > test.txt
/data # ls
test.txt
/data # cat test.txt
test ssh volume
/data # echo "sync unbuntu2 data" >> test.txt
/data # cat test.txt
test ssh volume
sync unbuntu2 data
这时数据同步到ubuntu1和ubuntu3上了看效果:
ubuntu3:
ubuntu1:
结语
容器中的数据存储算是比较重要的内容,本人水平有限,也只能分享到这里,如有问题请指点。