欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Docker入门之Dockerfile篇

程序员文章站 2022-06-03 15:52:33
...

前面我们使用docker commit只做了一个自己的镜像,但是这个主要适用于学习以及一些特殊的用途,实际应用一般不会通过这种方式制作镜像.因为docker commit只是把变动的文件中纸做成了镜像,比如之前文章中创建test镜像的时候,记录的是创建的/test这个文件夹,但是变动单而过程无法记录,没有办法追踪到我是通过mkdir /test这个命令创建的/test目录.这样的话就非常不利于追踪历史和维护,如果要是涉及到编译安装软件等等过程,那就更加让人崩溃了.其实对我们来说更重要的是想记录mkdir /test这条命令,因为只要有这条命令我们就是肯定可以得到/test目录这个结果.
FROM和RUN
因此 Docker 推出了 Dockerfile 来解救猿类;
Dockerfile 就是一个名叫 Dockerfile 的普通的文本文件;
用来记录制作镜像的命令;
我们找个空白文件夹建一个 Dockerfile 文件;
我们之前制作镜像的时候分为 3 步;
1.先是拉取镜像;

docker pull ubuntu

那在 Dockerfile 文件中则是通过 FROM 关键字;

FROM ubuntu

2.接着是执行了 mkdir /test 命令;
那在 Dockerfile 文件中则是通过 RUN 关键字;

FROM ubuntu

RUN mkdir /test

3.执行 docker commit 制作镜像
这一步不体现在 Dockerfile 中了;
需要使用 docker build 命令;

docker build -t baijunyao/test:v2

-t baijunyao/test:v2 : 定义镜像的名称和版本
. : build 的上下文环境目录
build 完成后 push 镜像就跟之前一样;
这里就略过不讲了;
Docker入门之Dockerfile篇
敲黑板划重点注意我标注的箭头了;
接着我要拿它来讲解上面说的让人不太理解的上下文环境目录;
Docker 跟 MySQL 之类的一样也分为客户端和服务端;
干活的是服务端;
客户端主要起调用的作用;
在执行 docker build 的时候是要把文件都发给服务端引擎来 dockerd 处理;
所以这个 . 就是指定上下文目录的;
意思是把当前目录的所有文件都发送给 dockerd ;
为了验证上面所说;
我们把 /bin/bash 文件复制到当前目录再运行看看;

cp /bin/bash
docker build -t baijunyao//test:v2

Docker入门之Dockerfile篇
可以看到发送的数据明显变多了;
但是其实 build 镜像的时候并不需要在这个 bash 文件;
这种发送全部文件的行为明显是不合理的;
要解决这个问题可以借助 .dockerignore ;
看名字就明显一股浓浓的山寨 .gitignore 的气息;
这正是 Docker 的高明之处;
什么 pull 、push、commit 等等通通使用我们已经熟知的 Git 概念;
极大的减少了我们的学习阻碍;
接着创建 .dockerignore 文件并加入 bash ;

/bash

再次执行 build 命令;
Docker入门之Dockerfile篇
另外针对上面的 RUN 顺便提一嘴;
如果要 RUN 多个命令可以用 && 连接;
比如说常见的 apt 安装;

apt update && apt install busybox

CMD
CMD关键字用来指定容器启动后执行的默认命令,修改下 Dockerfile 定义一个 CMD 输出 “Dockerfile CMD Commmand” ;
Dockerfile

FROM ubuntu

RUN mkdir /test

CMD echo "Dockerfile CMD Commmand"

build 镜像;

docker build -t baijunyao/test:v3 .

启动容器:

docker run baijunyao/test:v3

可以看到在run完后会出输出这句话:
Docker入门之Dockerfile篇
但是注意上面我说过是默认命令;
既然叫默认值那一般是可以替换的;
如果我们在启动容器的时候执行了其他命令;
那就不会再执行 CMD 默认命令了;

docker run baijunyao/test:v3 /bin/echo "test"

Docker入门之Dockerfile篇
可以看到 CMD 的默认命令没有执行;
不同于 RUN 可以定义多个;
CMD 只可以定义一个;
如果定义多个那么后面的会覆盖前面的 CMD;
ENTRYPOINT
那如果想定义一个不会被覆盖的命令就可以使用 ENTRYPOINT ;
Dockerfile

FROM ubuntu

RUN mkdir /test

CMD echo "Dockerfile CMD Commmand"

ENTRYPOINT echo "Dockerfile ENTRYPOINT Commmand"

我们来重新 build ;

docker build -t baijunyao/test:v4 .

运行容器:

docker run baijunyao/test:v4

Docker入门之Dockerfile篇
截图会出乎你的意料;
“Dockerfile CMD Commmand” 或者说 “test” 都没有输出;
“test” 会覆盖掉 “Dockerfile CMD Commmand” 是可以想到的;
但是连 “test” 都不输出这就有点说不过去了;
这里其实是模式的不同;
RUN 、 CMD、ENTRYPOINT 三种关键字都有两种模式;
1.shell 模式 这就是上面我们一直用的模式;
跟在命令行输入命令一样;
2.exec 模式
把上面的 shell 改成 exec 模式的话就如下所示;

['/bin/eho','test']

结论来了在 shell 模式下 ENTRYPOINT 会直接覆盖掉 CMD 的命令;
那我们用 exec 模式改写下;
Dockerfile

FROM ubuntu

RUN mkdir /test

CMD ["/bin/echo", "Dockerfile CMD Commmand"]

ENTRYPOINT ["/bin/echo", "Dockerfile ENTRYPOINT Commmand"]

再次build运行

docker build -t baijunyao/test:v5 .
docker run baijunyao/test:v5

Docker入门之Dockerfile篇
结果再次出乎意料;
CMD 的命令并没有正常输出;
原因是在 exec 模式下;
CMD 命令会被作为 ENTERPOINT 的参数;
因此上面的命令等于下面这样;

ENTRYPOINT ["/bin/echo", "Dockerfile ENTRYPOINT Commmand", "/bin/echo test"]

这真是为难人了;
那有木有一种方案可以让我正常的符合直觉的同时使用 CMD 和 ENTERPOINT ;
有;但是需要我先把下个关键字讲了;
COPY
COPY关键字用来将宿主机的文件 copy 到镜像中;
我们上面的 ENTERPOINT 代码转移到文件中;
enterpoint.sh

#!/bin/bash

echo "Dockerfile ENTRYPOINT Commmand"

exec "aaa@qq.com"

注意这里多加了一行 exec “aaa@qq.com” ;
它 的作用是执行接到的参数;
给予写权限;

chmod +x enterpoint.sh

接着改写下 Dockerfile 文件;
Dockerfile

FROM ubuntu

RUN mkdir /test

COPY enterpoint.sh /root/docker/

CMD echo "Dockerfile CMD Commmand"

ENTRYPOINT ["/root/docker/enterpoint.sh"]

上面这几条命令也很容易理解了;
先把宿主机上的 enterpoint.sh 文件复制到容器的 /root/docker/ 目录下;

再次 build 运行

docker build -t baijunyao/test:v6 .
docker run baijunyao/test:v6

Docker入门之Dockerfile篇
终于如愿以偿;
这里需要再次强调上下文环境;
我们 build 命令使用的是 . ;
也就是说我们不能获取当前目录外面的内容;
我们来试着把 /bin/bash 文件复制到镜像中;
Dockerfile

FROM ubuntu

RUN mkdir /test

COPY enterpoint.sh /root/docker/
COPY /bin/bash /root/docker/

CMD echo "Dockerfile CMD Commmand"

ENTRYPOINT ["/root/docker/enterpoint.sh"]

果然是报错的.
Docker入门之Dockerfile篇
ADD
ADD 和 COPY 差不多;
比较常见的场景是可以用来解压缩文件;
除了需要解压文件;
这个关键字就记住一句话就行了;
官方推荐用 COPY ;
并不建议用 ADD;
ENV
ENV 用来定义变量;
用空格或者 = 定义;
如果值中有空格就加引号;
比如:

ENV PATH /root/baijunyao
ENV PATH=/root/baijunyao
ENV NAME "Junyao Bai"
ENV NAME="Junyao Bai"

使用变量的时候加上 $ 即可;

FROM ubuntu

ENV DOCKER_PATH=/root/docker

RUN mkdir $DOCKER_PATH

COPY enterpoint.sh $DOCKER_PATH/

CMD echo "Dockerfile CMD Commmand"

ENTRYPOINT ["/root/docker/enterpoint.sh"]

另外需要注意的是有几个系统的变量不能作为 key;
比如说 PATH 、 HOME ;
这类变量可以直接在 Dockerfile 中使用;
Docker入门之Dockerfile篇
EXPOSE
EXPOSE 的主要作用就是告诉使用者镜像的守护端口;
比如说 MySQL 的 Dockerfile 就会定义 EXPOSE 为 3306 来告诉用户端口号;

相关标签: Dockerfile