Dockerfile语法及构建简单镜像

Dockerfile语法及构建简单镜像

Dockerfile语法及构建简单镜像


前面使用过docker commit去构建镜像

Docker并不建议用户通过这种方式构建镜像。原因如下:

这是一种手工创建镜像的方式,容易出错,效率低且可重复性弱。比如要在 debian base 镜像中也加入 vi,还得重复前面的所有步骤。
更重要的:使用者并不知道镜像是如何创建出来的,里面是否有恶意程序。也就是说无法对镜像进行审计,存在安全隐患。
既然 docker commit 不是推荐的方法,我们干嘛还要花时间学习呢?

原因是:即便是用 Dockerfile(推荐方法)构建镜像,底层也 docker commit 一层一层构建新镜像的。学习 docker commit 能够帮助我们更加深入地理解构建过程和镜像的分层结构。

准备构建镜像


需要创建一个Dockerfile文件,文件名必须是这个

[root@localhost ~]# vim Dockerfile
# 添加
FROM centos
RUN yum -y install httpd
RUN yum -y install net-tools
RUN yum -y install elinks
CMD ["/bin/bash"]

构建镜像


使用docker build进行镜像的构建,最后需要指定Dockerfile文件所在路径

[root@localhost ~]# docker build -t chai/centos-http-net /root
Successfully built 09266c896243
Successfully tagged chai/centos-http-net:latest

看到最后输出两条Successfully则构建成功。

它会根据文件中写的内容,使用centos镜像实例化一个容器,进入容器中执行三个yum命令

查看已经构建好的镜像

[root@localhost ~]# docker images
REPOSITORY             TAG       IMAGE ID       CREATED          SIZE
chai/centos-http-net   latest    09266c896243   10 seconds ago   581MB

使用新的镜像实例化容器

[root@localhost ~]# docker run -it --rm --name test chai/centos-http-net /bin/bash
[root@50da58a03736 /]# ifconfig # 命令可以运行即成功

镜像构建过程


在构建命令执行时输出的一大堆信息中,是执行Dockerfile中的每一行,最关键的几行信息如下

Step 1/5 : FROM centos  # 调用centos
 ---> 5e35e350aded   # centos镜像id

Step 2/5 : RUN yum install httpd -y
 ---> Running in a16ddf07c140  # 运行一个临时容器来执行install httpd
Removing intermediate container a16ddf07c140  # 完成后删除临时的容器id
 ---> b51207823459  # 生成一个镜像

Step 3/5 : RUN yum install net-tools -y
 ---> Running in 459c8823018a # 运行一个临时容器执行install net-tools
Removing intermediate container 459c8823018a # 完成后删除临时容器id
 ---> 5b6c30a532d4  # 再生成一个镜像

Step 4/5 : RUN yum install elinks -y
 ---> Running in a2cb490f9b2f  # 运行一个临时容器执行install elinks
Removing intermediate container a2cb490f9b2f # 完成后删除临时容器id
 ---> 24ba4735814b # 生成一个镜像

Step 5/5 : CMD ["/bin/bash"]
 ---> Running in 792333c88ba8  # 运行临时容器,执行/bin/bash
Removing intermediate container 792333c88ba8  # 完成后删除临时容器id
 ---> 09266c896243  # 生成镜像
Successfully built 09266c896243  # 最终成功后的镜像id就是最后生成的镜像id

每一步生成一个镜像,都属于一个docker commit的执行结果

在这个过程中一共生成了三个镜像层,都会被存储在graph中,包括层与层之间的关系,查看docker images中生成的镜像id是否为最后生成的镜像id,FROM和CMD都不算做镜像层

[root@localhost ~]# docker images
REPOSITORY             TAG       IMAGE ID       CREATED          SIZE
chai/centos-http-net   latest    09266c896243   10 seconds ago   581MB

通过docker history也可以看到简单的构建过程,这几个过程的size容量加起来也就是最终生成镜像的大小,也可将这里的镜像id和上面过程中的id进行对比,我们所看到的是三个yum就是形成的三个镜像层

[root@localhost ~]# docker history chai/centos-http-net:latest 
IMAGE         CREATED          CREATED BY                                      SIZE    COMMENT
09266c896243  17 minutes ago   /bin/sh -c #(nop) CMD ["/bin/bash"] 0B 
24ba4735814b  17 minutes ago   /bin/sh -c yum install elinks -y                121MB               
5b6c30a532d4  18 minutes ago   /bin/sh -c yum install net-tools -y             112MB               
b51207823459  18 minutes ago   /bin/sh -c yum install httpd -y                 145MB               
5e35e350aded  4 months ago     /bin/sh -c #(nop) CMD ["/bin/bash"] 0B 
<missing>     4 months ago     /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B 
<missing>     4 months ago     /bin/sh -c #(nop) ADD file:45a381049c52b5664… 203MB

镜像的缓存特性


之前文档中说到,nginx和httpd两个镜像都是基于debian系统制作的镜像,所以会使用相同的一部分镜像层去安装,而这个镜像被docker所共享,只需要下载一次即可

还是重新下载这两个镜像看一下是怎么进行使用

下载nginx镜像

[root@localhost ~]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
68ced04f60ab: Pull complete 
28252775b295: Pull complete 
a616aa3b0bf2: Pull complete 

下载httpd镜像

[root@localhost ~]# docker pull httpd
Using default tag: latest
latest: Pulling from library/httpd
68ced04f60ab: Already exists  # 已经存在
35d35f1e0dc9: Pull complete 
8a918bf0ae55: Pull complete 
d7b9f2dbc195: Pull complete 
d56c468bde81: Pull complete 

仔细观察下载的内容中,有两个id是一样的68ced04f60ab,在下载httpd的时候,会报出已经存在,因为在下载nginx时已经下载过了,这是因为这两个程序都是基于debian操作系统制作的镜像,这里就将这个id的镜像共享使用了,也就时使用了同一个镜像的缓存

自己构建的镜像也是具有这个缓存的特性的,在前面构建了一个镜像chai/centos-http-net,那我们根据构建这个镜像的Dockerfile文件的基础上进行一点点小的修改

效仿hello-world最小镜像的方法,也就是构建一个文档的镜像

[root@localhost ~]# vim testfile
# 添加内容
This is a FeiYi's file

然后将这个文件COPY到Dockerfile文件中,在之前的Dockerfile进行修改

[root@localhost ~]# vim Dockerfile
FROM centos
RUN yum -y install httpd
RUN yum -y install net-tools
RUN yum -y install elinks
COPY testfile /   # 将物理机当前目录的testfile文件复制到镜像中的根目录
CMD ["/bin/bash"]

将以上Dockerfile进行构建镜像,发现构建的很快,也没有很多的输出信息,而且除了新加的复制文件以外,其他的三个RUN镜像层,都多了一行Using cache,这是因为他们形成的镜像层已经作为缓存被构建的这个镜像使用

[root@localhost ~]# docker build -t test /root
Sending build context to Docker daemon  4.334MB
Step 1/6 : FROM centos
 ---> 5e35e350aded
Step 2/6 : RUN yum -y install httpd
 ---> Using cache
 ---> f01998ebc3ff
Step 3/6 : RUN yum -y install net-tools
 ---> Using cache
 ---> a3c39d101d8b
Step 4/6 : RUN yum -y install elinks
 ---> Using cache
 ---> 7c705388a610
Step 5/6 : COPY testfile /
 ---> 2111837dd86c
Step 6/6 : CMD ["/bin/bash"]
 ---> Running in 36782884ce16
Removing intermediate container 36782884ce16
 ---> 7b529720bf53
Successfully built 7b529720bf53
Successfully tagged test:latest

镜像层还有一个特点


如果改变了Dockerfile中镜像层的顺序,

[root@localhost ~]# vim Dockerfile
FROM centos
RUN yum -y install httpd
COPY testfile /
RUN yum -y install net-tools
RUN yum -y install elinks
CMD ["/bin/bash"]

然后进行构建,就会发现所有的内容全部重新构建

[root@localhost ~]# docker build -t test2 /root

这个特点就时层与层之间的关系,他们第一次构建时的顺序可以理解为他们之间的层级关系,如果执行顺序不一样,那么他们之间的层级关系也不一样,就不会去使用缓存构建。他们之间的关系被存储在graphDB中,如果读取不到相同的关系,是不会去使用缓存的。

–no-cache

–no-cache可以指定你构建镜像时,不适用已经存在的镜像层,也就是不使用缓存的特性

使用该参数重新构建刚才的Dockerfile

[root@localhost ~]# docker build -t test3 /root --no-cache

这样就会将所有的Dockerfile中的镜像层重新下载构建,而不是使用缓存。

Dockerfile文件排错方法


当个构建镜像时Dockerfile中报错,先来制作一个错误的Dockerfile

[root@localhost ~]# vim Dockerfile
FROM busybox
RUN touch tmpfile
RUN /bin/bash -c echo "continue to build..."
COPY testfile /

使用这个Dockerfile去构建镜像

[root@localhost ~]# docker build -t testerror /root
Sending build context to Docker daemon  4.336MB
Step 1/4 : FROM busybox
 ---> 83aa35aa1c79
Step 2/4 : RUN touch tmpfile
 ---> Running in 41a8dad29cd6
Removing intermediate container 41a8dad29cd6
 ---> 8cd5c9a720bb
Step 3/4 : RUN /bin/bash -c echo "continue to build..."
 ---> Running in bc1849fa8144
/bin/sh: /bin/bash: not found
The command '/bin/sh -c /bin/bash -c echo "continue to build..."' returned a non-zero code: 127

发现构建镜像过程中出现了报错/bin/sh: /bin/bash: not found

可以看到报错信息是从第三步才开始的,说明前两步是没有问题的,可以通过进入前两步最后结束的镜像id中去查看错误,进入前两层的镜像id是一个正常的容器环境,将第三步无法执行的命令,在容器中运行,将会看到真正的错误是没有/bin/bash这个环境

[root@localhost ~]# docker run -it 8cd5c9a720bb
/ # /bin/bash -c echo "continue to build..."
sh: /bin/bash: not found

因为构建这个镜像使用的是busybox,它使用的环境是/bin/sh

修改后,重新构建成功

[root@localhost ~]# docker build -t testerror /root
Successfully built ae5870fff063
Successfully tagged testerror:latest

运行容器后,可以看到创建的tmpfile和复制testfile

[root@localhost ~]# docker run -it testerror 
/ # ls
bin       etc       proc      sys       tmp       usr
dev       home      root      testfile  tmpfile   var

Dockerfile文件语法


常用构建镜像指令

FROM  # 指定base镜像
MAINTAINER # 指定镜像作者,后面根任意字符串
COPY # 把文件从host复制到镜像内
  COPY src dest
  COPY ["src","dest"]
  src:只能是文件
ADD # 用法和COPY一样,唯一不同时src可以是压缩包,表示解压缩到dest位置,src也可以是目录
ENV # 设置环境变量可以被接下来的镜像层引用,并且会加入到镜像中
  ENV MY_VERSION 1.3
  RUN yum -y install http-$MY_VERSION
  # 当进入该镜像的容器中echo $MY_VERSION会输出1.3
EXPOSE # 指定容器中的进程监听的端口(接口),会在docker ps -a中的ports中显示
  EXPOSE 80
VOLUME # 容器卷,后面会讲到,把host的路径mount到容器中
  VOLUME /root/htdocs /usr/local/apahce2/htdocs
WORKDIR # 为后续的镜像层设置工作路径
        # 如果不设置,Dockerfile文件中的每一条命令都会返回到初始状态
        # 设置一次后,会一直在该路经执行之后的分层,需要WORKDIR /回到根目录
CMD # 启动容器后默认运行的命令,使用构建完成的镜像实例化为容器时,进入后默认执行的命令
    # 这个命令会被docker run启动命令替代
    # 如:docker -it --rm centos echo "hello"
    # echo "hello"会替代CMD运行的命令
  CMD ["nginx", "-g", "daemon off"]  # 该镜像实例化后的容器,进入后运行nginx启动服务
ENTRYPOINT # 容器启动时运行的命令,不会被docker run的启动命令替代

RUN/CMD/ENTRYPOINT区别


在语法中说到CMD和ENTRYPOINT是容器启动后和容器启动时,运行的命令,RUN是构建镜像时运行的命令。三者的区别究竟在什么地方,通过一个例子来看

使用这三者来构建一个镜像

[root@localhost ~]# vim Dockerfile 
# 添加
FROM centos
RUN yum -y install net-tools
CMD echo "hello chai"
ENTRYPOINT echo "hello mupei"
[root@localhost ~]# docker build -t chai /root
Successfully built 10dec59bba24
Successfully tagged chai:latest

构建完成后,运行镜像

[root@localhost ~]# docker run -it chai
hello mupei

很明显在构建构成中RUN已经完成了它的工作

RUN:执行命令并创建新的镜像层,主要用于安装软件包

而在运行镜像后,只输出了hello mupei,是ENTRYPOINT来执行的命令

这两个都算作是启动指令,也就是必须启动容器才会去执行的指令,一般用来启动运行程序使用

结论:当ENTRYPOINT和CMD同时存在时,ENTRYPOINT生效

ENTRYPOINT和CMD使用格式

shell和exec两种格式

shell格式

CMD command、ENTRYPOINT command

shell格式会始终调用一个shell程序去执行命令

通过一个例子来看

[root@localhost ~]# vim Dockerfile 
FROM centos
RUN yum -y install net-tools
ENV name chai
ENTRYPOINT echo "hello $name"
[root@localhost ~]# docker build -t pei /root
Successfully built cf475e27d587
Successfully tagged pei:latest
[root@localhost ~]# docker run -it pei
hello chai

当指令执行时,shell会调用/bin/bash

exec格式

CMD [“命令”, “选项”, “参数”]、ENTRYPOINT [“命令”, “选项”, “参数”]

exec格式下无法去调用ENV定义的变量,如果非要让exec格式去读取变量的话,它的命令的位置就要使用一个shell环境。因为变量的读取就是使用shell去读取的。
如:ENTRYPOINT [“/bin/sh”, “-c”, “echo hello,$变量名”]

再看一个例子

[root@localhost ~]# vim Dockerfile 
FROM centos
RUN ["yum", "-y", "install", "net-tools"]
CMD ["/bin/echo", "hello chai"]
ENTRYPOINT ["/bin/echo", "hello world"]
[root@localhost ~]# docker build -t feiyi /root
Successfully built 52189c01eaf5
Successfully tagged feiyi:latest

运行容器后,只有ENTRYPOINT的命令是正常执行了,输出了我们需要的hello world,

ENTRYPOINT [“/bin/echo”, “hello world”]:这一行中,/bin/echo是命令,hello world是执行的参数

而CMD中的/bin/echo和hello chai都做为结果输出,并没有被当做命令

[root@localhost ~]# docker run -it feiyi
hello world /bin/echo hello chai

结论:当使用exec格式时,ENTRYPOINT的第一个参数被识别为命令,CMD的参数按顺序变为ENTRYPOINT命令的参数

这个结论相当于Dockerfile文件中的以下两行=echo “hello world /bin/echo hello chai”

CMD [“/bin/echo”, “hello chai”]

ENTRYPOINT [“/bin/echo”, “hello world”]

即:

[root@localhost ~]# docker run -it feiyi
hello world /bin/echo hello chai
[root@localhost ~]# echo "hello world /bin/echo hello chai"
hello world /bin/echo hello chai
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/101826.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 群晖 docker 端口_群晖 l2tp

    群晖 docker 端口_群晖 l2tp最近群晖docker某个容器端口总是被扫描,系统自动封禁了很多IP,遂想更改端口,个人对Linux和docker非常不熟悉,只做记录,如有错误,欢迎指出流程分为以下几个步骤:停止容器修改端口映射重启docker停止容器首先用dockerps命令查看所有的容器名称和id例如容器ID为a1b2c3用dockerstopa1b2c3来停止容器修改端口映射修改端口映射主要在了两个文件hostconfig.json和config.v2.json下面是如何找到这两个文件的路径利用cd/v

    2022年10月18日
  • Docker(三)- 从镜像运行启动容器「建议收藏」

    Docker(三)- 从镜像运行启动容器「建议收藏」文章目录从镜像运行启动容器容器启动后运行的命令`ENTRYPOINT`和`CMD`启动容器时覆盖`ENTRYPOINT`和`CMD“-d`后台运行`dockerexec`进入容器,运行指定命令`–name`和`–restart=always“–rm`和`dockercp`从镜像运行启动容器从一个镜像可以运行启动一个或多个容器。所谓容器,我们可以理解为是一个虚拟的计算机,其中运行着操作系统,操作系统中运行着我们部署的应用。从tomcat镜像启动容器:do

  • docker重新部署项目_docker一键部署

    docker重新部署项目_docker一键部署Docker为什么出现?阿里云镜像加速、HelloWorld流程底层原理、部署项目1、安装docker2、安装Docker-Compose3、编写相关配置文件Docker-Compose.yml编写Dockerfile编写Nginx的配置4、打包前端后端Docker简介Docker为什么出现?一款产品:开发–上线两套环境!应用环境,应用配置!开发—运维。问题:我在我的电脑上可以运行!版本更新,导致服务不可用

    2022年10月18日
  • docker(6)镜像的使用「建议收藏」

    docker(6)镜像的使用「建议收藏」前言Docker的三大核心概念:镜像、容器、仓库。初学者对镜像和容器往往分不清楚,学过面向对象的应该知道类和实例,这跟面向对象里面的概念很相似我们可以把镜像看作类,把容器看作类实例化后的对象。|

  • docker vscode远程调试_为什么要使用docker

    docker vscode远程调试_为什么要使用docker### 简介code server是一款开源的桌面软件vs code浏览器实现,可以让你随时随地的写代码,只要打开浏览器就可以进入到工作环境,将代码放在云端十分方便。### 安装流程只需要在docker环境下安装即可,输入如下命令安装即可,第一次安装会慢一点。dockerrun-itd–namemycode-p8081:8080-v/data/mycode:/home/coder/project-uroot-ePASSWORD=123

  • docker 常用命令大全

    docker 常用命令大全Docker常用命令大全

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号