docker 的 Dockerfile使用

简介

Dockerfile是由一系列命令和参数构成的脚本,这些命令应用于操作系统(centos或者Ubuntu)基础镜像并最终创建的一个新镜像

在没有Dockerfile之前用手工的方式,修改配置文件,或者添加,删除文件目录的方式,来构建一种新镜像;这种手工方式麻烦,容易出错,而且不能复用;

如果有Dockerfile,用脚本方式来构建自动化,可复用的,高效率的创建镜像方式

再软件系统开发生命周期中,采用Dockerfile来构建镜像;

1、对于开发人员:可以为开发团队提供一个完全一致的开发环境;

2、对于测试人员:可以直接拿开发时所构建的镜像或者通过Dockerfile文件构建一个新的镜像开始工作;

3、对于运维人员:在部署时,可以实现应用的无缝移植。

DockerFile常用指令

指令 含义
FROM image_name:tag 定义了使用哪个基础镜像启动构建流程
MAINTAINER user_info 声明镜像维护者信息
LABEL key value 镜像描述元信息(可以写多条)
ENV key value 设置环境变量(可以写多条)
RUN command 构建镜像时需要运行的命令(可以写多条)
WORKDIR path_dir 设置终端默认登录进来的工作目录
EXPOSE port 当前容器对外暴露出的端口
ADD source_dir/file dest_dir/file 将宿主机的文件复制到容器内,如果是一个压缩文件,将会在复制后自动解压
COPY source_dir/file dest_dir/file 和ADD相似,但是如果有压缩文件是不能解压
VOLUME 创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等
CMD 指定容器启动时要运行的命令,假如有多个CMD,最后一个生效
ENTRYPOINT 指定容器启动时要运行的命令
ONBUILD 当构建一个被继承的Dockerfile时运行的命令,父镜像在被子镜像继承后父镜像的onbuild被触发。可以把ONBUID理解为一个触发器。

构建镜像命令

docker build 命令用于使用 Dockerfile 创建镜像。

语法

docker build [OPTIONS] PATH | URL | -

OPTIONS说明:

  • --build-arg=[] :设置镜像创建时的变量;
  • --cpu-shares :设置 cpu 使用权重;
  • --cpu-period :限制 CPU CFS周期;
  • --cpu-quota :限制 CPU CFS配额;
  • --cpuset-cpus :指定使用的CPU id;
  • --cpuset-mems :指定使用的内存 id;
  • --disable-content-trust :忽略校验,默认开启;
  • -f :指定要使用的Dockerfile路径;
  • --force-rm :设置镜像过程中删除中间容器;
  • --isolation :使用容器隔离技术;
  • --label=[] :设置镜像使用的元数据;
  • -m :设置内存最大值;
  • --memory-swap :设置Swap的最大值为内存+swap,"-1"表示不限swap;
  • --no-cache :创建镜像的过程不使用缓存;
  • --pull :尝试去更新镜像的新版本;
  • --quiet, -q :安静模式,成功后只输出镜像 ID;
  • --rm :设置镜像成功后删除中间容器;
  • --shm-size :设置/dev/shm的大小,默认值是64M;
  • --ulimit :Ulimit配置。
  • --squash :将 Dockerfile 中所有的操作压缩为一层。
  • --tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。
  • --network: 默认 default。在构建期间设置RUN指令的网络模式

[选看]docker build 命令后点的意思 . 号的意思

我们在使用 docker build 命令去构建镜像时,往往会看到命令最后会有一个 . 号。

那么这里的 . 号代表什么意思呢?

在我们学习对 . 号的理解有所偏差,以为是用来指定 Dockerfile 文件所在的位置的,但其实 -f 参数才是用来指定 Dockerfile 的路径的,那么 . 号究竟是用来做什么的呢?

Docker 在运行时分为 Docker引擎(服务端守护进程) 以及 客户端工具,我们日常使用各种 docker 命令,其实就是在使用客户端工具与 Docker 引擎 进行交互。

那么当我们使用 docker build 命令来构建镜像时,这个构建过程其实是在 Docker引擎 中完成的,而不是在本机环境。

那么如果在 Dockerfile 中使用了一些 COPY 等指令来操作文件,如何让 Docker引擎 获取到这些文件呢?

这里就有了一个 镜像构建上下文 的概念,当构建的时候,由用户指定构建镜像的上下文路径,而 docker build 会将这个路径下所有的文件都打包上传给 Docker 引擎,引擎内将这些内容展开后,就能获取到所有指定上下文中的文件了。

比如说 dockerfile 中的 COPY ./package.json /project,其实拷贝的并不是本机目录下的 package.json 文件,而是 docker引擎 中展开的构建上下文中的文件,所以如果拷贝的文件超出了构建上下文的范围,Docker引擎 是找不到那些文件的。

所以 docker build 最后的 . 号,其实是在指定镜像构建过程中的上下文环境的目录。

关于基础镜像

我们看到dockerFile 都会有一句FROM xxx ,这句话的意思就是基于那个基础镜像创建,那问题来了,最开始没有镜像怎么构建呢?

官方提供了两种方式

  • 使用tar创建一个完整的镜像(一般用于系统打包构建镜像)

  • 从头开始创建简单的镜像(FROM scratch 类似于对象的基类)

参考:https://docs.docker.com/develop/develop-images/baseimages/

这里我们只使用第二种方式

DockerFile构建最简单自定义镜像,输入hello docker images

需求:

构建最简单的镜像,输出hello docker images

参考:

https://github.com/docker-library/hello-world/

生成输出hello docker images的可执行c文件

安装环境

安装:gcc glibc glibc-static

yum install -y gcc glibc glibc-static

编写hello.c文件

文件内容

#include<stdio.h>

int main()
{
    printf("hello docker images\n");
}

编译生成可执行文件

gcc -static hello.c -o hello

image-20210107095856476

测试输出

./hello

image-20210107095948115

编写DockerFile脚本

vi myHello

文件内容

#从最小镜像 scratch 构建,scratch类似于基类
#意思是不依赖任何base镜像
FROM scratch
#添加文件到根目录
ADD hello /
#执行根目录的hello命令
CMD ["/hello"]

保存退出

构建镜像

#注意镜像名称必须小写
#注意最后有个点
docker build -f myHello -t makalo/myhello:1.0 .

构建完成

image-20210107100314272

使用自定义镜像启动容器测试

docker run makalo/myhello:1.0

执行结果

image-20210107100607058

但是这种是比较麻烦的,一般我们基于镜像库里面创建好的镜像构建自定义镜像更方便

例:DockerFile构建自定义centos镜像

需求:

基于centos构建并安装net-tools和vim两个工具

编写DockerFile脚本

Dokcerfile 是一个普通的文本文件,文件名一般叫 Dockerfile

也可以不用

这里我们创建 myCentosDockerFile 文件不用加任何后缀,写入下面内容

#基于centos镜像创建
FROM centos
#声明镜像维护者信息
MAINTAINER makalo<makalochen@foxmail.com>
#镜像描述元信息 多个元信息 用 | 隔开 太长需要换行的话则使用\符号,不需要换行用|
LABEL name="makalo CentOS Image"|build-date="20210106"
#设置环境变量
ENV WORKPATH /home/
#设置终端默认登录进来的工作目录并引用上面设置的环境变量
WORKDIR $WORKPATH

#构建镜像时需要运行的命令  安装两个工具
RUN yum -y install net-tools
RUN yum -y install vim

#当前容器对外暴露出的端口
EXPOSE 80

#指定容器启动时要运行的命令
CMD /bin/bash

构建镜像

例:

#使用当前目录下的myCentosDockerFile文件构建一个镜像
#镜像名为makalo/mycentos,tag为1.1 注意最后有个点
docker build -f myCentosDockerFile -t makalo/mycentos:1.1 .

构建完成

image-20210106191647766

使用自定义镜像启动容器测试

docker run -it makalo/mycentos:1.1

如图工具已经装上

image-20210106191907314

DockerFile通过VOLUME实现容器卷

说明:前面用启动命令 -v 宿主机目录:容器卷目录 来实现容器卷目录挂载

但是由于定义Dockerfile的时候,并不能保证在所有的宿主机上都有这样的特定目录,

所以在Dockerfile定义中,只能指定容器卷目录,宿主机目录不能自定义

命令

VOLUME['/home/v1','/home/v2']

编写dockkerFile

FROM centos

VOLUME ["/home/v1","/home/v2"]

CMD /bin/bash

构建镜像

docker build -f myVolumeDockerFile -t makalo/mytest:1.1 .

使用镜像创建容器测试

docker run -it makalo/mytest:1.1

测试

容器内的文件夹

image-20210107105451885

正常

然后我们通过 docker inspect 容器ID 来查看下默认生成的容器卷对应的宿主机目录

或者直接过滤显示的

docker inspect -f "{{ .Mounts }}"  容器id1 容器id2

例:

docker inspect -f "{{ .Mounts }}" 7092b859d9c5

下图圈起来的就是容器卷在宿主机的位置

image-20210107111353562

我们创建一个文件

image-20210107111604109

查看容器

image-20210107111628707

完成同步,没问题

个人认为,要实现容器卷,还是通过 -v 启动命令,用dockerfile方式,比较操蛋,宿主机目录不能自定义

DockerFile中CMD, ENTRYPOINT 的区别和联系

  • CMD, ENTRYPOINT都是容器启动的时候,执行执行命令

  • 都支持exec和shell方式;

  • 一般用法,是单独一个CMD,或者先ENTRYPOINT,后CMD结合使用

  • 假如有多个CMD,启动的时候带命令参数,会覆盖前面的CMD命令,最后一个命令生效,

所以我们平时用CMD的时候,有一种情况的就是单独一个CMD命令即可,启动命令也不带参数;

单独CMD方式(tomcat 官方dockerFile)

先看下tomcat的官方镜像的dockerfile;

image-20210107113427750

由于CMD命令假如有多个,会被覆盖 ,只有最后一个执行;所以我们测试启动

docker run -it -p 8080:8080 tomcat

image-20210107113649678

像这种启动不带参数 是没毛病的,tomcat可以正常启动;

但是我们如果加上参数 比如 /bin/bash 或者 ls -l

那就会覆盖dockerfile中的CMD ["catalina.sh","run"],tomcat就不执行

如:

docker run -it -p 8080:8080 tomcat ls -l

image-20210107113926944

CMD和ENTRYPOINT结合使用(redis 官方dockerFile)

我们再看下redis官方dockerfile

image-20210107114433881

这种就是第二种常见用法,先搞个ENTRYPOINT 执行下执行命令,然后后面跟CMD来拼接具体的执行参数;

最终执行的 是

docker-entrypoint.sh redis-server

经过上面两个例子,相信大伙对CMD, ENTRYPOINT 有一定的了解;

我们再看看详细语法

CMD语法

CMD ["executable","param1","param2"] (exec form, this is the preferred form)

CMD ["param1","param2"] (as default parameters to ENTRYPOINT)

CMD command param1 param2 (shell form)

第一种用法:运行一个可执行的文件并提供参数。

第二种用法:为ENTRYPOINT指定参数。

第三种用法(shell form):是以”/bin/sh -c”的方法执行的命令。

ENTRYPOINT 语法

ENTRYPOINT [“executable”, “param1”, “param2”] (exec 格式, 推荐)

ENTRYPOINT command param1 param2 (shell 格式)

我们一般开发 官方都建议用 exec格式;好使

下面通过一些简单例子,来具体看下CMD, ENTRYPOINT 的实际用法

案例

单独CMD

第一个dockerfile

FROM centos

CMD echo "abc"

#CMD ["/bin/echo", "defg"]

我们可以运行测试,看看结果,以及把第二个CMD去掉 看看结果;

以及 运行命令后面跟运行参数 看看结果;能明白CMD多个的话 只有最后一个生效;

构建镜像参考

docker build -f test1 -t makalo/test1:1.0 .

启动容器运行结果

image-20210107115551382

多个CMD

image-20210107115630494

CMD和ENTRYPOINT结合

第二个dockerfile

FROM centos

ENTRYPOINT ["ls"]

CMD ["-l"]

这个dockerfile 我们用 ENTRYPOINT 执行一个ls命令 然后CMD 追加参数

相当于这个CMD是默认的一个参数 假如需要更换参数 启动的时候 ,我们直接替换即可

构建镜像参考

docker build -f test2 -t makalo/test2:1.0 .

运行结果

默认参数

image-20210107120042539

带参数(会替换原来的默认)

image-20210107120247649

DockerFile之ONBUILD的使用

简介

ONBUILD:当构建一个被继承的Dockerfile时运行的命令,父镜像在被子镜像继承后父镜像的onbuild被触发。可以把ONBUID理解为一个触发器
格式:

ONBUILD <其它指令>

ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等,而这些指令,

在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。

啥意思呢?

案例

看个小例子

1.新建base文件,写入下面内容

FROM centos
#子镜像会执行此命令
ONBUILD RUN yum -y install net-tools
CMD /bin/bash

2.新建child文件,写入下面内容

FROM base

3.构建两个镜像

先构建父镜像

docker build -f base -t base .

构建子镜像

docker build -f child -t child .

image-20210107172249212

4.运行容器测试

docker run -it child

image-20210107175217891

子镜像运行了父镜像ONBUILD 内容,已经安装了net-tools

posted @ 2021-01-07 17:55  makalo  阅读(1126)  评论(0编辑  收藏  举报