测试 Dockerfile VOLUME 容器卷,另外测试 Dockerfile 其他命令的记录

Docker 容器卷
Dockerfile
Dockerfile再总结
Dockerfile 命令详解—官方文档
Dockerfile 最佳实践—官方文档

说明

以下注释符号,均使用“#===>”

宿主机称为:Linux
Docker 容器称为:容器
容器 是 Linux 上安装了 Docker 服务之后,运行镜像产生的。

准备工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@localhost ~]# cd /linux/ #===> 进入 Linux 目录
[root@localhost linux]#
[root@localhost linux]#
[root@localhost linux]# ll
总用量 39628
-rw-r--r--. 1 root root 40575538 11月 23 17:50 junit.jar #===> 启动之后,访问 curl 127.0.0.1:8080/hello 返回 "Hello World!"
[root@localhost linux]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
java 8 d23bdf5b1b1b 3 years ago 643MB
[root@localhost linux]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@localhost linux]#
[root@localhost linux]#
[root@localhost linux]# PS1=------------------------ #===> 修改 bash 提示符,方便查看:[root@localhost linux]# 改为 ------------------------
------------------------

测试1:ADD,CMD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
------------------------vim junit
------------------------cat -n junit
1 FROM java:8
2
3 ADD junit.jar /app.jar
4
5 CMD java -jar /app.jar
------------------------#===> -f 指定 Dockerfile 文件,如果文件名是 Dockerfile 可以不用指定
------------------------#===> -t 指定名称和版本号,默认版本 latest
------------------------#===> 注意后面有个 . 表示当前路径下找 junit Dockerfile 文件
------------------------docker build -f junit -t junit .
Sending build context to Docker daemon 40.58MB
Step 1/3 : FROM java:8
---> d23bdf5b1b1b
Step 2/3 : ADD junit.jar /app.jar
---> 5a437a969a92
Step 3/3 : CMD java -jar /app.jar
---> Running in c7027a7a0197
Removing intermediate container c7027a7a0197
---> 19ccea4d5fa8
Successfully built 19ccea4d5fa8 #===> 镜像ID
Successfully tagged junit:latest #===> 镜像名称和版本号
------------------------
------------------------docker images #===> 打包镜像,多了一个 junit:latest 19ccea4d5fa8
REPOSITORY TAG IMAGE ID CREATED SIZE
junit latest 19ccea4d5fa8 34 seconds ago 684MB
java 8 d23bdf5b1b1b 3 years ago 643MB
------------------------#===> 运行镜像
------------------------#===> --name junit01 指定容器名称
------------------------#===> -d 容器后台运行
------------------------#===> junit 镜像名称。默认版本号 latest
------------------------docker run --name junit01 -d junit
d1d992d2c7f2ff2c56374c371e7175edcb710f6a5a74af25a8829758d4c6b679
------------------------
------------------------docker ps -a #===> 容器运行起来
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d1d992d2c7f2 junit "/bin/sh -c 'java -j…" 6 seconds ago Up 5 seconds junit01
------------------------
------------------------docker exec -it junit01 bash #===> 进入容器
root@d1d992d2c7f2:/# curl 127.0.0.1:8080/hello
Hello World! #===> 成功访问
root@d1d992d2c7f2:/#
root@d1d992d2c7f2:/# ls #===> 注意,这个是我们 ADD 添加到 根目录底下的 app.jar 文件
app.jar bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@d1d992d2c7f2:/# exit #===> 退出容器
exit
------------------------

》》》总结:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
     #  基础镜像
1 FROM java:8
2
# 把 Linux 当前目录下的 junit.jar,拷贝到容器根目录 / 下面,改名为 app.jar
# docker build 命令后面跟个 . 小数点,这个 junit.jar 就在 . 下面找
# docker build 命令后面跟个 /linux,这个 junit 就在 /linux 下面找
# 例如,下面两者效果相同。
# [root@localhost linux]# docker build -f junit -t junit .
# [root@localhost /]# docker build -f /linux/junit -t junit /linux/
3 ADD junit.jar /app.jar
4
# CMD 是容器内执行的命令
5 CMD java -jar /app.jar


“FROM 基础镜像”
#===> 指定基础镜像,一个 Dockerfile 只能指定一次 FROM,这没什么好说的。

“ADD Linux路径 Container路径”
#===> 容器外执行的命令。
#===> Linux路径:
若是绝对路径(以 / 开头),这没什么好说的,就去Linux去找就行了。
若为相对路径(不以 / 开头),这时候就会以 docker build 尾部的路径去找,一般为 . 小数点,也就是执行docker build 这个命令时所在的路径。
下面两者执行效果相同
[root@localhost linux]# docker build -f junit -t junit .
[root@localhost /]# docker build -f /linux/junit -t junit /linux/
“CMD 命令”
#===> 容器内执行的命令。

测试2:VOLUME

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
------------------------vim junit
------------------------cat -n junit
1 FROM java:8
2
3 VOLUME /container1
4
5 ADD junit.jar /app.jar
6
7 CMD java -jar /app.jar
------------------------
------------------------docker rm -f junit01 ; docker build -f junit -t junit .
junit01
Sending build context to Docker daemon 40.58MB
Step 1/4 : FROM java:8
---> d23bdf5b1b1b
Step 2/4 : VOLUME /container1
---> Running in 99d9e14b0223
Removing intermediate container 99d9e14b0223
---> 44c139f30b2a
Step 3/4 : ADD junit.jar /app.jar
---> ca38032e5eb2
Step 4/4 : CMD java -jar /app.jar
---> Running in 83da9bff836f
Removing intermediate container 83da9bff836f
---> b587a7320bbc
Successfully built b587a7320bbc
Successfully tagged junit:latest
------------------------#===> 会把旧的镜像 REPOSITORY、TAG 设为 <none>,镜像ID还在,所以旧的镜像还可用,可用镜像ID方式启动容器
------------------------#===> 会把新的镜像 REPOSITORY、TAG 设为 junit:latest
------------------------docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
junit latest b587a7320bbc 9 seconds ago 684MB
<none> <none> 19ccea4d5fa8 16 hours ago 684MB
java 8 d23bdf5b1b1b 3 years ago 643MB
------------------------
------------------------docker run --name junit01 -d junit
1d9c718e3cc5f92193e95ab99c75e5d15d758a8a1d28669d668a437bc245cbe7
------------------------
------------------------docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1d9c718e3cc5 junit "/bin/sh -c 'java -j…" 3 seconds ago Up 2 seconds junit01
------------------------
------------------------docker exec -it junit01 bash
root@1d9c718e3cc5:/# ls #===> / 底下多了一个空目录 container1
app.jar bin boot container1 dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@1d9c718e3cc5:/# ls container1/
root@1d9c718e3cc5:/#
root@1d9c718e3cc5:/# exit
exit
------------------------

》》》总结:

1
2
3
“VOLUME 容器目录”
#===> 容器目录可以不存在,会自动创建
#===> 只是声明卷,默认匿名挂载:docker run 会默认自带“-v 容器目录”功能

测试3:RUN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
------------------------vim junit
------------------------cat -n junit
1 FROM java:8
2
3 VOLUME /container1
4
5 RUN mkdir /run-mkdir
6
7 ADD junit.jar /app.jar
8
9 CMD java -jar /app.jar
------------------------
------------------------docker rm -f junit01 ; docker build -f junit -t junit .
junit01
Sending build context to Docker daemon 40.58MB
Step 1/5 : FROM java:8
---> d23bdf5b1b1b
Step 2/5 : VOLUME /container1
---> Using cache
---> 44c139f30b2a
Step 3/5 : RUN mkdir /run-mkdir
---> Running in 977c2084d687
Removing intermediate container 977c2084d687
---> 307bf514ce61
Step 4/5 : ADD junit.jar /app.jar
---> 2836cfd28c9d
Step 5/5 : CMD java -jar /app.jar
---> Running in f6a8a71098c6
Removing intermediate container f6a8a71098c6
---> eb20a0657de2
Successfully built eb20a0657de2
Successfully tagged junit:latest
------------------------docker run --name junit01 -d junit ; docker exec -it junit01 bash
40cb69e54b1d4fbe27ff50505440468c09fa14a2357212f8bbde19dd6921e0b4
root@40cb69e54b1d:/# ls #===> 根目录 / 下面存在 run-mkdir 目录
app.jar bin boot container1 dev etc home lib lib64 media mnt opt proc root run run-mkdir sbin srv sys tmp usr var
root@40cb69e54b1d:/#
root@40cb69e54b1d:/# ls container1/
root@40cb69e54b1d:/#
root@40cb69e54b1d:/# exit
exit
------------------------
------------------------ls / #===> Linux 下没有 run-mkdir 目录
app bin boot data dev etc home lib lib64 linux media mnt opt proc root run sbin srv sys tmp usr var
------------------------
------------------------ll
总用量 39632
-rw-r--r--. 1 root root 103 11月 24 19:50 junit
-rw-r--r--. 1 root root 40575538 11月 23 17:50 junit.jar
------------------------

总结:

1
2
3
“RUN 命令”
#===> 该命令会在容器内部执行。
#===> "RUN mkdir /run-dir" 会在容器根目录 / 下面建立一个 run-dir 空目录

测试4:VOLUME

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
------------------------vim junit
------------------------cat -n junit
1 FROM java:8
2
3 VOLUME /container1
4
5 RUN touch /container1/a1
6
7 ADD junit.jar /app.jar
8
9 CMD java -jar /app.jar
------------------------
------------------------docker rm -f junit01 ; docker build -f junit -t junit . > /dev/null
junit01
------------------------
------------------------docker run --name junit01 -d junit
08a9ae8f0d4a40b88d6e951fb95311ac20405d43a025e64a4c28fe893d6510a6
------------------------
------------------------docker exec -it junit01 ls /
app.jar boot dev home lib64 mnt proc run srv tmp var
bin container1 etc lib media opt root sbin sys usr
------------------------
------------------------docker exec -it junit01 ls /container1
------------------------#===> 没有任何文件

》》》总结:

1
2
3
4
“VOLUME /container1”
#===> 虽然容器启动后,创建了目录 /container1,但是只要容器启动才会创建这个目录。
#===> 而在这个命令后面使用这个目录,这个目录是不存在的
#===> 要想使用,可以参照测试5:先创建目录,再创建文件,最后再挂载这个目录。

测试5:Dockerfile 内使用 VOLUME 目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
------------------------vim junit
------------------------
------------------------cat -n junit
1 FROM java:8
2
3 RUN mkdir /container1
4
5 RUN touch /container1/file1
6
7 VOLUME /container1
8
9 RUN touch /container1/a1
10
11 ADD junit.jar /app.jar
12
13 CMD java -jar /app.jar
------------------------
------------------------docker rm -f junit01 ; docker build -f junit -t junit . > /dev/null
junit01
------------------------docker run --name junit01 -d junit ; docker exec -it junit01 ls /container1
0c69f86e7843755ff150124f9214c9235bae2256a8f575ff268b045939202a95
file1 #===> 存在文件
------------------------

测试6:ARG,ENV

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
------------------------vim junit
------------------------cat -n junit
1 FROM java:8
2
3 ENV xxx=/a
4
5 RUN mkdir ${xxx}
6
7 ARG volume1=/container1
8
9 RUN mkdir ${volume1}
10
11 RUN touch ${volume1}/file1
12
13 VOLUME ${volume1}
14
15 ADD junit.jar /app.jar
16
17 CMD java -jar /app.jar
------------------------
------------------------docker rm -f junit01 ; docker build -f junit -t junit . > /dev/null
junit01
------------------------#===> -v Linux路径:容器路径 进行挂载(会把容器路径下内容清空,把Linux的内容放进去)
------------------------docker run --name junit01 -v /linux/a1:/container1 -d junit ; docker exec -it junit01 bash
b190b7f8008ceaecdf3a059e33bd08022acd6f490cc46d0671c573346e465122
root@b190b7f8008c:/# ls #===> 存在目录 a
a app.jar bin boot container1 dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@b190b7f8008c:/# ls container1/ #===> 只存在文件 1。说明 Linux 会覆盖这个目录的内容
1
root@b190b7f8008c:/# set | grep volume #===> 不存在此变量
root@b190b7f8008c:/# set | grep xxx
xxx=/a
root@b190b7f8008c:/# env | grep xxx #===> 环境变量
xxx=/a
root@b190b7f8008c:/# exit
exit
------------------------#===> -A 把匹配的后 10 行也显示出来。-B 则是把匹配之前x行显示出来。
------------------------docker inspect junit01 | grep Mounts -A 10
"Mounts": [
{
"Type": "bind",
"Source": "/linux/a1",
"Destination": "/container1",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
"Config": {
------------------------

》》》总结:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
“ENV xxx=/a”
#===> 定义变量,在Docker 容器中生效,也可在 Dockerfile 中生效
“ARG volume1=/container1”
#===> 定义变量,仅在 Dockerfile 命令中使用
“VOLUME ${volume1}
#===> 只是做对卷的一个声明。如果目录不存在,容器启动时会创建。
#===> 声明卷可以放在 Dockerfile 的尾部。
#===> 真正挂载卷是使用“docker run -v Linux路径:容器路径”来挂载的。
#===> 1. 匿名挂载:-v 容器路径
#===> 如果指定了 VOLUME path1,docker run 则默认 -v path1 使用匿名挂载

#===> 2. 具名挂载:-v 卷名:容器路径
------------------------docker run --name junit01 -v xxx:/container1 -d junit
ed1237af10ace9f234bc8438f4c294a1c6be07c4d8c6b0bb9a4190285354b68d
------------------------
------------------------docker volume ls
DRIVER VOLUME NAME
local 9d92b715153382098754a94985e34dc303ff76d4a1b001ddcacd1a6b56ef95f7
...
local f83303b09e11034f578f6a8165d0e095b8d4eeb598d82a7fa486489ee0f65aac
local xxx #===> 我们的具名挂载
------------------------#===> 查看卷详情
------------------------docker volume inspect xxx
[
{
"CreatedAt": "2020-11-25T00:12:26+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/xxx/_data",
"Name": "xxx",
"Options": null,
"Scope": "local"
}
]
------------------------#===> 通过容器详情,查看卷详情
------------------------docker inspect junit01 | grep Mounts -A 10
"Mounts": [
{
"Type": "volume",
"Name": "xxx",
"Source": "/var/lib/docker/volumes/xxx/_data",
"Destination": "/container1",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
------------------------
#===> 3. 指定路径挂载:-v Linux路径:容器路径(上面已经演示)

测试7:WORKDIR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
------------------------ vim junit
------------------------ cat -n junit
1 FROM java:8
2
3 RUN touch a
4
5 WORKDIR /app
6
7 RUN touch b
8
9 ADD junit.jar /app.jar
10
11 CMD java -jar /app.jar
------------------------
------------------------ docker rm -f junit01 ; docker build -f junit -t junit . > /dev/null
junit01
------------------------
------------------------ docker run --name junit01 -d junit ; docker exec -it junit01 bash
3a7bcba186d1ee41641fbb767fb0bd0e512f7b272a31a7678518e934e8740905
root@3a7bcba186d1:/app# ls #===> /app 下存在文件 b
b
root@3a7bcba186d1:/app#
root@3a7bcba186d1:/app# ls / #===> / 下存在文件 a
a app app.jar bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@3a7bcba186d1:/app#
root@3a7bcba186d1:/app# exit
exit
------------------------

》》》总结:

1
2
3
“WORKDIR /app”
#===> 等同于“RUN mkdir -p /app ; cd /app”,创建并进入目录。
#===> 默认 WORKDIR 是 / 根目录。

测试8:ENV,-e

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
------------------------vim junit
------------------------cat -n junit
1 FROM java:8
2
3 ENV TPF1="taopanfeng1"
4
5 ENV TPF2="taopanfeng2"
6
7 ADD junit.jar /app.jar
8
9 CMD java -jar /app.jar
------------------------
------------------------docker rm -f junit01 ; docker build -f junit -t junit . > /dev/null
junit01
------------------------#===> -e env1="value1" -e env2="value2" 可以覆盖 ENV 的环境变量
------------------------docker run --name junit01 -e TPF1="-sdk-da=-=sdjmsdf" -e TPF2="=`1kd" -d junit ; docker exec -it junit01 env | grep TPF
> ^C
------------------------docker run --name junit01 -e TPF1="-sdk-da=-=sdjmsdf" -e TPF2="-=1kd" -d junit ; docker exec -it junit01 env | grep TPF
7461ff5a264d3acfa815fc6078843b9ca74a8690a4a536a2641890980a0d11a0
TPF1=-sdk-da=-=sdjmsdf
TPF2=-=1kd
------------------------

》》》总结:

1
2
“ENV TPF1="taopanfeng1"
#===> docker run -e env1="value1" -e env2="value2" 可以覆盖 ENV 的环境变量

来个 VOLUME 总结

1
2
3
4
5
6
7
“VOLUME 容器路径”

只是对于卷的定义,如果目录不存在,容器会在启动时自动创建,并使用匿名挂载。
最好声明卷,定义在 Dockerfile 尾部,避免其他地方使用到。

真正常用的是,使用指定路径挂载“docker run -v Linux路径:容器路径”来指定的。
这个会把容器路径下的内容清空,把Linux路径下的东西放入容器路径。