volume用于将容器内的数据在本地进行持久化,这一节我们以redis为例用实际操作来看看几种创建volume的方式。
文章目录
- 操作环境
- 原始镜像
- 声明volume
- 创建volume
- 指定容器目录不指定宿主机目录
- 指定容器目录指定宿主机目录
- 总结
操作环境
- Centos 7.9.2009
- Docker version 20.10.2, build 2291f61
原始镜像
直接下载最新的redis镜像
docker pull redis
[root@testmachine fuhanxiao]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 621ceef7494a 13 days ago 104MB
然后直接启动
[root@testmachine fuhanxiao]# docker run -p 6379:6379 -d redis
1ab8246b84bc23afbd8348deecd74e4260d457e6dd987917bec8b1ddb3f1afdf
需要注意的是因为做了端口映射,必须要满足下面两个
- 必须要有firewalld或者是iptables服务在跑,默认centos7使用的是firewalld,
systemctl status firewalld
- 必须要打开ip forward,
echo 1 > /proc/sys/net/ipv4/ip_forward
查看下容器信息
[root@testmachine fuhanxiao]# docker inspect 1ab
...
...
"Mounts": [
{
"Type": "volume",
"Name": "d31bb6212a374dd34a93a992274f3a522cd1555c554ccf497277c3c89a668f5b",
"Source": "/var/lib/docker/volumes/d31bb6212a374dd34a93a992274f3a522cd1555c554ccf497277c3c89a668f5b/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
...
...
可以看到原始镜像配置了一个volume,Destination
对应的是容器内的目录/data
,Source
对应的是宿主机的目录,这里的目录名是一个随机数。
这其实是镜像内暴露了一个volume出来,但是起容器的时候没有声明本地对应的目录,所以docker就自己新建了一个目录作为对应的volume,下面我还会用自己新建的volume对这一理论进行验证。
声明volume
下面我们在原始镜像的基础上开始自己的实验。
创建Dockerfile
内容如下
FROM redis
COPY ./redis.conf /usr/local/etc/redis/redis.conf
VOLUME /usr/local/etc/redis
CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
主要是这里的第三行,通过VOLUME
关键字将容器内部的一个目录声明成一个volume。需要注意的是Dockfile里面只能声明容器内部的目录,因为生成的镜像可能会被运行在各种设备上,所以宿主机的目录一定是在起容器的时候指定和生成的。
同时这里还拷贝了一份本地的配置文件到容器内,作为新镜像的启动配置,下面我们就用这个文件来进行测试。
生成新的镜像
[root@testmachine myredis]# docker build -t redis:v2 .
Sending build context to Docker daemon 190.5kB
Step 1/4 : FROM redis
---> 621ceef7494a
Step 2/4 : COPY ./redis.conf /usr/local/etc/redis/redis.conf
---> fdd3cb20855c
Step 3/4 : VOLUME /usr/local/etc/redis
---> Running in a95a0f7fad1c
Removing intermediate container a95a0f7fad1c
---> 884d688cb230
Step 4/4 : CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
---> Running in 9badfe62c79d
Removing intermediate container 9badfe62c79d
---> ca75e474ccf3
Successfully built ca75e474ccf3
Successfully tagged redis:v2
[root@testmachine myredis]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
redis v2 ca75e474ccf3 10 minutes ago 104MB
redis latest 621ceef7494a 2 weeks ago 104MB
下面我们用这个新的v2镜像来测试。
创建volume
指定容器目录不指定宿主机目录
如上面看到的那样,指定了容器的目录但是不指定宿主机目录的话,docker会自动在宿主机上创建对应的目录。
其中指定容器目录的方式有两种:
通过命令行
[root@testmachine myredis]# docker run -p 6379:6379 -v /usr/local/etc/redis -d redis
af3aeeb1071d3fd2470fa3174803fc73c0872033f5e2e44c25144368f0281aa3
注意这里用的镜像是默认镜像redis,并不是redis:v2,然后通过-v
参数声明了容器内的一个目录作为volume。
注意,为了避免错误,volume的目录路径最好是完整路径
下面查看下容器绑定的volume
[root@testmachine myredis]# docker inspect af3
...
...
"Mounts": [
{
"Type": "volume",
"Name": "b7b0151ded2be4f71dab89ef1ce5289be2785917106d71efe35a0ca2e07aadf8",
"Source": "/var/lib/docker/volumes/b7b0151ded2be4f71dab89ef1ce5289be2785917106d71efe35a0ca2e07aadf8/_data",
"Destination": "/usr/local/etc/redis",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "d9b95843b00e4f4861668b79dcd8261b78a233c2c5ee4a588a664e4950730361",
"Source": "/var/lib/docker/volumes/d9b95843b00e4f4861668b79dcd8261b78a233c2c5ee4a588a664e4950730361/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
...
...
可以看到docker同样创建了一个随机目录,进入这个目录,新建一个文件写点内容
[root@testmachine myredis]# cd /var/lib/docker/volumes/b7b0151ded2be4f71dab89ef1ce5289be2785917106d71efe35a0ca2e07aadf8/_data
[root@testmachine _data]# echo 123 > test.txt
然后去容器内就马上能看到了
[root@testmachine fuhanxiao]# docker exec -it af3 /bin/bash
root@af3aeeb1071d:/data# cd /usr/local/etc/redis/
root@af3aeeb1071d:/usr/local/etc/redis# cat test.txt
123
通过镜像写死
第二种方式就像Dockerfile那样写死一个目录,然后直接起容器就可以了
[root@testmachine fuhanxiao]# docker run -p 6379:6379 -d redis:v2
63a0edc424287c02c6ac57f7a81e390bba6ba7a5e45051dc6f48839cde3f5ce6
查看绑定和上面类似,除了路径名不同,就不重复验证了。
指定容器目录指定宿主机目录
docker随机创建目录显然不太利于数据和文件的管理,所以最好还是认为指定宿主机的目录。值得一提的是,指定宿主机目录一定要有对应的容器目录,不管这个容器目录在Dockerfile有没有声明过都可以。
[root@testmachine fuhanxiao]# docker run -p 6379:6379 -v /home/fuhanxiao/myredis/v2:/usr/local/etc/redis -d redis:v2
612734ee9c1a0291d70f408867ef7333f8d0840cd3f461297b3dd9a872f08389
这里我在v2目录下也弄了一个配置文件,容器起的时候会将宿主机的内容复制到容器内,但是注意容器起了以后两边不管那边有修改都能双向同步。
注意,如果宿主机的配置文件有错误会导致容器起不来,如果宿主机的目录是空的容器起来之后volume的目录也会是空的
总结
总结下相关的知识点:
- 指定容器内的路径可以在镜像内写死或者是起容器时候指定
- 指定宿主机的路径只能在起容器时指定,而且必须和容器内路径一起指定
- 用绝对路径
- 起容器的时候会将宿主机的内容拷贝同步到容器内(docker建立的随机目录不算),之后是双向同步
- 内容的更新都是实时热更新
我是T型人小付,一位坚持终身学习的互联网从业者。