【重识云原生】第六章容器6.1.10节——DockerFile解析

2022-09-30 15:38:42 浏览数 (1)

1 Dockfile详解

1.1 什么是Dockerfile

        首先通过一张图来了解 Docker 镜像、容器和 Dockerfile 三者之间的关系。

        通过上图可以看出使用 Dockerfile 定义镜像,运行镜像启动容器。

        Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

        镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。完整镜像的结构图:

        Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。

示例:

代码语言:javascript复制
docker build -f /path/Dockerfile

        由Image Builder顺序执行各指令,从而完成Image构建:

1.2 Docker commit 那么方便,为什么要用 DockerFile ?

        此前我构建镜像都是使用 Docker commit,简单又明确。但是一段时间后我就犯迷糊了,commit 出来的镜像都是什么啊,那个描述信息真的会有人很详细的去写?镜像多了自己都记不住了。而且,commit 到底打包了些什么东西啊?我后来突然意识到。是像虚拟机快照那样吗?会把当时的容器状态全都打包进去吗?还是说只是单纯的打包一下当时的文件?

        Docker commit 一个很不方便的地方就在于,难以回顾它是怎么来的,比方说我这里使用 commit 构建了一个 CentOS vim 镜像,查看它的构建历史发现:

代码语言:javascript复制
[root@centos7 ~]# docker history centos-vim:v1.0
IMAGE               CREATED             CREATED BY                                      SIZE
8df43092cfd1        6 minutes ago       /bin/bash                                      140MB
67591570dd29        4 years ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>           4 years ago         /bin/sh -c #(nop)  LABEL name=CentOS Base Im…   0B
<missing>           4 years ago         /bin/sh -c #(nop) ADD file:940c77b6724c00d42…  192MB
<missing>           4 years ago         /bin/sh -c #(nop)  MAINTAINER https://github…   0B

Docker commit 的弊端:

  • 如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,如果不小心清理,将会导致镜像极为臃肿。
  • 使用 Docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体在操作的。虽然 docker diff 或许可以告诉得到一些线索,但是远远不到可以确保生成一致镜像的地步。这种黑箱镜像的后期维护工作是非常痛苦的。
  • 镜像所使用的分层存储,除当前层外,之前的每一层都是不会发生改变的,换句话说,任何修改的结果仅仅是在当前层进行标记、添加、修改,而不会改动上一层。如果使用 Docker commit 制作镜像,以及后期修改的话,每一次修改都会让镜像更加臃肿一次,所删除的上一层的东西并不会丢失,会一直如影随形的跟着这个镜像,即使根本无法访问到。这会让镜像更加臃肿。

用Docker build 方式镜像生成新的镜像

        Docker build 的方式生成新镜像的前提条件是有一个旧的基础镜像,在此基础上通过 Docker build 命令执行dockerfile 文件从而生成一个新的镜像,不同于Docker commit,是镜像–> 镜像的转化。当然,是否转化正确是需要将镜像 Docker run起来。

Dockerfile的优点:

  • 能够自由灵活的与宿主机联系,比如,某些配置文件在宿主机验证并使用过后很好用,那么,可以将文件copy到镜像中,(这个动作是写在dockerfile里),add 远程主机的配置文件到镜像中,定义onbuild动作等等各种灵活的功能。docker commit不能做到这些事情,因为是在一个封闭的在运行中的容器中,无法做复制拷贝宿主机文件的事情。
  • dockerfile本身就是一个比较详细的构建文档,有这个文档就可以清楚的知道新构建的镜像经历了怎样的变化。没有黑箱操作的困扰了,后期的维护更为方便了。
  • 后期可扩展性强,一个文件就可以在哪都可以运行镜像了。(前提有网,有安装docker环境)

Dockerfile的缺点:

  • 编写不容易,因为需要对脚本这些比较了解,有Linux基础的人才可以编写出好用的dockerfile,有一定入门门槛。

1.3 docker build工作原理简述

        首先需要有一个制作镜像的目录,该目录下有个文件,名称必须为Dockerfile,Dockerfile有指定的格式,#号开头为注释,指令默认用大写字母来表示,以区分指令和参数,docker build读取Dockerfile是按顺序依次Dockerfile里的配置,且第一条非注释指令必须是FROM 开头,表示基于哪个基础镜像来构建新镜像。可以根据已存在的任意镜像来制作新镜像。

.dcokerignore:把文件路径写入到.dockerignore,对应的路径将不会被打包到新镜像

        docker build命令用于从Dockerfile构建镜像。可以在docker build命令中使用-f标志指向文件系统中任何位置的Dockerfile。

docker build [选项] <上下文路径/URL/->

        docker build 后面的.表示当前目录,也是指定上下文的路径上下文,例如:

代码语言:javascript复制
docker build -f /path/to/a/Dockerfile

        Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API ,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。

        当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?

        这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。

        那么为什么会有人误以为.·是指定 Dockerfile 所在目录呢?这是因为在默认情况下,如果不额外指定 Dockerfile 的话,会将上下文目录下的名为 Dockerfile 的文件作为 Dockerfile。这只是默认行为,实际上 Dockerfile 的文件名并不要求必须为 Dockerfile,而且并不要求必须位于上下文目录中,比如可以用 -f …/Dockerfile 参数指定某个文件作为 Dockerfile。当然,一般大家习惯性的会使用默认的文件名 Dockerfile,以及会将其置于镜像构建上下文目录中。

1.4 Dockerfile的文件格式

        Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动执行指令。一开始必须要指明所基于的镜像名称,接下来一般会说明维护者信息;后面则是镜像操作指令,例如 RUN 指令。每执行一条RUN 指令,镜像添加新的一层,并提交;最后是 CMD 指令,来指明运行容器时的操作命令。

        用一张图来描述一下:

        两种类型的行

  • 以# 开头的注释行
  • 由专用“指令(Instruction)”开头的指令行

        Dockerfile文件格式实例如下:

代码语言:javascript复制
## Dockerfile文件格式
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# 1、第一行必须指定 基础镜像信息
FROM ubuntu
# 2、维护者信息
MAINTAINER docker_user docker_user@email.com
# 3、镜像操作指令
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "ndaemon off;" >> /etc/nginx/nginx.conf
# 4、容器启动执行指令
CMD /usr/sbin/nginx

1.5 构建镜像

        docker build 命令会根据 Dockerfile 文件及上下文构建新 Docker 镜像。构建上下文是指 Dockerfile 所在的本地路径或一个URL(Git仓库地址)。构建上下文环境会被递归处理,所以构建所指定的路径还包括了子目录,而URL还包括了其中指定的子模块。

        将当前目录做为构建上下文时,可以像下面这样使用docker build命令构建镜像:

代码语言:javascript复制
docker build . 
Sending build context to Docker daemon 6.51 MB 
...

        说明:构建会在 Docker 后台守护进程(daemon)中执行,而不是CLI中。构建前,构建进程会将全部内容(递归)发送到守护进程。大多情况下,应该将一个空目录作为构建上下文环境,并将 Dockerfile 文件放在该目录下。

        在构建上下文中使用的 Dockerfile 文件,是一个构建指令文件。为了提高构建性能,可以通过.dockerignore文件排除上下文目录下不需要的文件和目录。

        在 Docker 构建镜像的第一步,docker CLI 会先在上下文目录中寻找.dockerignore文件,根据.dockerignore 文件排除上下文目录中的部分文件和目录,然后把剩下的文件和目录传递给 Docker 服务。

        Dockerfile 一般位于构建上下文的根目录下,也可以通过-f指定该文件的位置:

代码语言:javascript复制
docker build -f /path/to/a/Dockerfile .

        构建时,还可以通过-t参数指定构建成镜像的仓库、标签。

1.6 镜像标签

代码语言:javascript复制
docker build -t nginx/v3 .

        如果存在多个仓库下,或使用多个镜像标签,就可以使用多个-t参数:

代码语言:javascript复制
docker build -t nginx/v3:1.0.2 -t nginx/v3:latest .

        在 Docker 守护进程执行 Dockerfile 中的指令前,首先会对 Dockerfile 进行语法检查,有语法错误时会返回:

代码语言:javascript复制
docker build -t nginx/v3 . 
Sending build context to Docker daemon 2.048 kB 
Error response from daemon: Unknown instruction: RUNCMD

1.6 缓存

1.6.1 缓存机制

        Docker 守护进程会一条一条的执行 Dockerfile 中的指令,而且会在每一步提交并生成一个新镜像,最后会输出最终镜像的ID。生成完成后,Docker 守护进程会自动清理你发送的上下文。 Dockerfile文件中的每条指令会被独立执行,并会创建一个新镜像,RUN cd /tmp等命令不会对下条指令产生影响。 Docker 会重用已生成的中间镜像,以加速docker build的构建速度。以下是一个使用了缓存镜像的执行过程:

代码语言:javascript复制
$ docker build -t svendowideit/ambassador .
Sending build context to Docker daemon 15.36 kB
Step 1/4 : FROM alpine:3.2
---> 31f630c65071
Step 2/4 : MAINTAINER SvenDowideit@home.org.au
---> Using cache
---> 2a1c91448f5f
Step 3/4 : RUN apk update &&      apk add socat &&        rm -r /var/cache/
---> Using cache
---> 21ed6e7fbb73
Step 4/4 : CMD env | grep _TCP= | (sed 's/.PORT([0-9])_TCP=tcp://(.):(.)/socat -t 100000000 TCP4-LISTEN:1,fork,reuseaddr TCP4:2:3 &/' && echo wait) | sh
---> Using cache
---> 7ea8aef582cc
Successfully built 7ea8aef582cc

        构建缓存仅会使用本地父生成链上的镜像,如果不想使用本地缓存的镜像,也可以通过--cache-from指定缓存。指定后将不再使用本地生成的镜像链,而是从镜像仓库中下载。

1.6.2 寻找缓存的逻辑

        Docker 寻找缓存的逻辑其实就是树型结构根据 Dockerfile 指令遍历子节点的过程。下图可以说明这个逻辑。

         大部分指令可以根据上述逻辑去寻找缓存,除了 ADD 和 COPY 。这两个指令会复制文件内容到镜像内,除了指令相同以外,Docker 还会检查每个文件内容校验(不包括最后修改时间和最后访问时间),如果校验不一致,则不会使用缓存。

        除了这两个命令,Docker 并不会去检查容器内的文件内容,比如 RUN apt-get -y update,每次执行时文件可能都不一样,但是 Docker 认为命令一致,会继续使用缓存。这样一来,以后构建时都不会再重新运行apt-get -y update。

        如果 Docker 没有找到当前指令的缓存,则会构建一个新的镜像,并且之后的所有指令都不会再去寻找缓存。

1.7 上下文路径

        上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。

        解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。

        如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。

注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。

2 Dockerfile常用指令

官方build参考

2.1 FROM

        指定基础镜像,必须为第一个命令,格式:   

代码语言:javascript复制
FROM <image>   

FROM <image>:<tag>   

FROM <image>@<digest>

示例:  

代码语言:javascript复制
FROM mysql:5.6

注: tag或digest是可选的,如果不使用这两个值时,会使用latest版本的基础镜像

2.2 MAINTAINER(新版即将废弃)

维护者信息

格式:

代码语言:javascript复制
MAINTAINER <name>

示例:

代码语言:javascript复制
MAINTAINER bertwu

MAINTAINER xxx@163.com

MAINTAINER bertwu xxx@163.com

2.3 RUN

        构建镜像时执行的命令.RUN用于在构建镜像时执行命令,其有以下两种命令执行方式:

  • shell执行

格式:  

代码语言:javascript复制
RUN <command>
  • exec执行

格式:

代码语言:javascript复制
RUN ["executable", "param1", "param2"]

示例:

代码语言:javascript复制
RUN ["executable", "param1", "param2"]

RUN apk update

RUN ["/etc/execfile", "arg1", "arg1"]

注:RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像, 可以在构建时指定--no-cache参数,如:docker build --no-cache

2.4 ADD

        将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget。

格式:

代码语言:javascript复制
ADD <src>... <dest>

ADD ["<src>",... "<dest>"]

用于支持包含空格的路径

示例:

代码语言:javascript复制
ADD hom* /mydir/              # 添加所有以"hom"开头的文件

ADD hom?.txt /mydir/        # ? 替代一个单字符,例如:"home.txt"

ADD test relativeDir/         # 添加 "test" 到 WORKDIR/relativeDir/

ADD test /absoluteDir/     # 添加 "test" 到 /absoluteDir/

2.5 COPY

        功能类似ADD,但是是不会自动解压文件,也不能访问网络资源。

2.6 CMD

        构建镜像后调用,也就是在容器启动时才进行调用。

格式:

代码语言:javascript复制
CMD ["executable","param1","param2"] (执行可执行文件,优先)

CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)

CMD command param1 param2 (执行shell内部命令)

示例:

代码语言:javascript复制
CMD echo "This is a test." | wc -l

CMD ["/usr/bin/wc","--help"]

注:CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。

2.7 ENTRYPOINT

        配置容器,使其可执行化。配合CMD可省去"application",只使用参数。

格式:

代码语言:javascript复制
ENTRYPOINT ["executable", "param1", "param2"] (可执行文件, 优先)

ENTRYPOINT command param1 param2 (shell内部命令)

示例:

代码语言:javascript复制
FROM ubuntu

ENTRYPOINT ["ls", "/usr/local"]

CMD ["/usr/local/tomcat"]

        之后,docker run 传递的参数,都会先覆盖cmd,然后由cmd 传递给entrypoint ,做到灵活应用。

注:ENTRYPOINT与CMD非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT, 而docker run命令中指定的任何参数,都会被当做参数再次传递给CMD。 Dockerfile中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的设置, 而只执行最后的ENTRYPOINT指令。 通常情况下,ENTRYPOINT 与CMD一起使用,ENTRYPOINT 写默认命令,当需要参数时候 使用CMD传参。

2.8 LABEL

        用于为镜像添加元数据。

格式:

代码语言:javascript复制
LABEL = = = ...

示例:

代码语言:javascript复制
LABEL version="1.0" description="这是一个Web服务器" by="IT笔录"

注:使用LABEL指定元数据时,一条LABEL指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条LABEL指令指定,以免生成过多的中间镜像。

2.9 ENV

        设置环境变量。

格式:

代码语言:javascript复制
ENV #之后的所有内容均会被视为其的组成部分,因此,一次只能设置一个变量

ENV = ... #可以设置多个变量,每个变量为一个"="的键值对,如果中包含空格,可以使用来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行

示例:

代码语言:javascript复制
ENV myName John Doe ENV myDog Rex The Dog ENV myCat=fluffy

2.10 EXPOSE

        指定于外界交互的端口。

格式:

代码语言:javascript复制
EXPOSE [...]

示例:

代码语言:javascript复制
EXPOSE 80 443 EXPOSE 8080 EXPOSE 11211/tcp 11211/udp

注:EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过-p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口。如果没有暴露端口,后期也可以通过-p 8080:80方式映射端口,但是不能通过-P形式映射。

2.11 VOLUME

        用于指定持久化目录(指定此目录可以被挂载出去)。

格式:

代码语言:javascript复制
VOLUME ["/path/to/dir"]

示例:

代码语言:javascript复制
VOLUME ["/data"] VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"

注:一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:

  1. 卷可以容器间共享和重用
  2. 容器并不一定要和其它容器共享卷
  3. 修改卷后会立即生效
  4. 对卷的修改不会对镜像产生影响
  5. 卷会一直存在,直到没有任何容器在使用它

2.12 WORKDIR

        工作目录,类似于cd命令。

格式:

代码语言:javascript复制
WORKDIR /path/to/workdir

示例:

代码语言:javascript复制
WORKDIR /a (这时工作目录为/a) 
WORKDIR b (这时工作目录为/a/b) 
WORKDIR c (这时工作目录为/a/b/c)

注:通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。在使用docker run运行容器时,可以通过-w参数覆盖构建时所设置的工作目录。

2.13 USER

        指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。使用USER指定用户时,可以使用用户名、UID或GID,或是两者的组合。当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户。

格式:  

代码语言:javascript复制
USER user  

USER user:group  

USER uid  

USER uid:gid  

USER user:gid  

USER uid:group

示例:   

代码语言:javascript复制
USER www

注:使用USER指定用户后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT都将使用该用户。镜像构建完成后,通过docker run运行容器时,可以通过-u参数来覆盖所指定的用户。

2.14 ARG

        用于指定传递给构建运行时的变量(给dockerfile传参),相当于构建镜像时可以在外部为里面传参。

格式:

代码语言:javascript复制
ARG [=]

示例:

代码语言:javascript复制
ARG site

ARG build_user=www

From centos:7

ARG parameter

VOLUME /usr/share/nginx

RUN yum -y install $parameter

EXPOSE 80 443

CMD nginx -g "daemon off;" # 可以这如下这样灵活传参 docker build --build-arg=parameter=net-tools -t nginx:01 .

2.15 ONBUILD

        用于设置镜像触发器。

格式: 

代码语言:javascript复制
ONBUILD [INSTRUCTION]

示例:

代码语言:javascript复制
ONBUILD ADD . /app/src   

ONBUILD RUN /usr/local/bin/python-build --dir /app/src

注:ONNBUID后面跟指令,当当前的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被钥触发。

3 制作镜像

  • 如果有多个RUN,自上而下依次运行,每次运行都会形成新的层,建议&& 放入一行运行
  • 如果有多个CMD,只有最后一个运行
  • 如果有多个Entrypoint,只有最后一个运行
  • 如果CMD和entrypoint共存,只有entrypoint运行,且最后的CMD会当做entrypoint的参数

        镜像制作分为两个阶段:

  1. docker build阶段 基于dockerfile制作镜像 (RUN,用于此阶段的运行命令)
  2. docker run阶段 基于镜像运行容器 (CMD,基于image run容器时候,需要运行的命令)
  3. docker build 基于第一阶段的镜像被别人from制作新镜像 (entrypoint 或onbuild 基于镜像重新构建新镜像时候在此阶段运行的命令)

3.1 源码编译制作nginx镜像

代码语言:javascript复制
# This my first nginx Dockerfile
# Version 1.0
# Base images 基础镜像
FROM centos
# MAINTAINER 维护者信息
MAINTAINER bertwu
# ENV 设置环境变量
ENV PATH /usr/local/nginx/sbin:$PATH
# ADD  文件放在当前目录下,拷过去会自动解压
ADD nginx-1.8.0.tar.gz /usr/local/
ADD epel-release-latest-7.noarch.rpm /usr/local/
# RUN 执行以下命令
RUN rpm -ivh /usr/local/epel-release-latest-7.noarch.rpm
RUN yum install -y wget lftp gcc gcc-c   make openssl-devel pcre-devel pcre && yum clean all
RUN useradd -s /sbin/nologin -M www
# WORKDIR 相当于cd
WORKDIR /usr/local/nginx-1.8.0
RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-pcre && make && make install
RUN echo "daemon off;" >> /etc/nginx.conf
# EXPOSE 映射端口
EXPOSE 80
# CMD 运行以下命令
CMD ["nginx"]

3.2 制作简单镜像

代码语言:javascript复制
root@ubuntu:~# mkdir myapp
root@ubuntu:~/myapp# vim Dockerfile # 编写Dockerfile
FROM alpine:3.15
LABEL Maintainer="bertwu bertwu6688@edu.com"
ADD hosts /etc/hosts
root@ubuntu:~/myapp# vim hosts # 编写文件
root@ubuntu:~/myapp# cat hosts
127.0.0.1 localhost
127.0.0.1 localhost.localdomain
172.100.100.100 xxx.com
root@ubuntu:~/myapp# docker image build .  # 构建镜像
Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM alpine:3.15
---> c059bfaa849c
Step 2/3 : LABEL Maintainer="bertwu bertwu6688@edu.com"
---> Running in 63324216f4ec
Removing intermediate container 63324216f4ec
---> bb69e6b659a2
Step 3/3 : ADD hosts /etc/hosts
---> 0d6e00e31ce6
Successfully built 0d6e00e31ce6
root@ubuntu:~/myapp# docker image tag 0d6e00e31ce6 myapp:1.0 # 添加标签
root@ubuntu:~/myapp# docker image ls # 查看镜像
REPOSITORY          TAG             IMAGE ID       CREATED              SIZE
myapp               1.0             0d6e00e31ce6   About a minute ago   5.59MB
root@ubuntu:~/myapp# docker image inspect myapp:1.0 | grep -i maint
"Maintainer": "bertwu bertwu6688@edu.com"
"Maintainer": "bertwu bertwu6688@edu.com"

3.3 自作1.1版本

        先编辑apk下载文件。

代码语言:javascript复制
root@ubuntu:~/myapp# vim repositories
https://mirrors.aliyun.com/alpine/v3.15/main
https://mirrors.aliyun.com/alpine/v3.15/community
root@ubuntu:~/myapp# cat Dockerfile
FROM alpine:3.15
LABEL Maintainer="bertwu bertwu6688@edu.com"
ADD repositories /etc/apk/repositories # 添加自己指定的repositories
RUN apk update && 
apk add nginx bash # 安装nginx与bash
root@ubuntu:~/myapp# docker run --name myapp -it --rm myapp:1.1 /bin/bash # 制作镜像
bash-5.1# which nginx
/usr/sbin/nginx
bash-5.1#
bash-5.1# nginx -v
nginx version: nginx/1.20.2
bash-5.1#
bash-5.1# cat /etc/apk/repositories
https://mirrors.aliyun.com/alpine/v3.15/main
https://mirrors.aliyun.com/alpine/v3.15/community

3.4 构建centos镜像

        下面通过编写Dockerfile文件来制作Centos镜像,并在官方镜像的基础上添加vim和net-tools工具。首先在/home/dockfile 目录下新建文件Dockerfile。然后使用上述指令编写该文件。

代码语言:javascript复制
# Dockerfile

[root@localhost dockerfile]# cat Dockerfile

FROM centos:7

MAINTAINER bertwu 1258398543@qq.com

ENV MYPATH /usr/local

WORKDIR $MYPATH

RUN yum -y install vim net-tools

EXPOSE 80

CMD /bin/bash

        逐行解释该Dockerfile文件的指令:

  • FROM centos:7 该image文件继承官方的centos7
  • ENV MYPATH /usr/local:设置环境变量MYPATH
  • WORKDIR $MYPATH:直接使用上面设置的环境变量,指定/usr/local为工作目录
  • RUN yum -y install vim && net-tools:在/usr/local目录下,运行yum -y install vim和yum -y install net-tools命令安装工具,注意安装后的所有依赖和工具都会打包到image文件中
  • EXPOSE 80:将容器80端口暴露出来,允许外部连接这个端口
  • CMD:指定容器启动的时候运行命令

        下面执行build命令生成image文件,如果执行成功,可以通过docker images来查看新生成的镜像文件。

代码语言:javascript复制
[root@localhost dockerfile]# docker build -t mycentos:1.0 .
[root@localhost dockerfile]# docker images
REPOSITORY    TAG             IMAGE ID       CREATED              SIZE
mycentos      1.0             e0316e2ed3a5   About a minute ago   409MB

        可以使用 docker history 镜像id 查看镜像构建过程。

代码语言:javascript复制
[root@localhost dockerfile]# docker history  e0316e2ed3a5
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
e0316e2ed3a5   2 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/bin…   0B
79738577ded0   2 minutes ago   /bin/sh -c #(nop)  EXPOSE 80                    0B
f10acdc62daf   2 minutes ago   /bin/sh -c yum -y install vim   net-tools       205MB
40b0252c02c7   3 minutes ago   /bin/sh -c #(nop) WORKDIR /usr/local            0B
d38940eb3b75   3 minutes ago   /bin/sh -c #(nop)  ENV MYPATH=/usr/local        0B
b23dc50b92b4   3 minutes ago   /bin/sh -c #(nop)  MAINTAINER bertwu <125839…   0B
eeb6ee3f44bd   2 months ago    /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>      2 months ago    /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B
<missing>      2 months ago    /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4…   204MB

        进入容器,看看是否能够执行ifconfig 及vim命令:

代码语言:javascript复制
root@localhost dockerfile]# docker run -it mycentos:1.0
[root@3143cb46b8c4 local]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255
ether 02:42:ac:11:00:02  txqueuelen 0  (Ethernet)
RX packets 7  bytes 586 (586.0 B)
RX errors 0  dropped 0  overruns 0  frame 0
TX packets 0  bytes 0 (0.0 B)
TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
inet 127.0.0.1  netmask 255.0.0.0
loop  txqueuelen 1000  (Local Loopback)
RX packets 0  bytes 0 (0.0 B)
RX errors 0  dropped 0  overruns 0  frame 0
TX packets 0  bytes 0 (0.0 B)
TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

3.5 构建springboot应用

代码语言:javascript复制
cat Dockerfile
FROM openjdk:8-jre # jar包基于jdk ,war包基于tomcat
WORKDIR /app
ADD demo-0.0.1-SNAPSHOT.jar app.jar # 将上下文中 jar包复制到 /app目录下,并且重命名为app.jar
EXPOSE 8081 # 暴露端口
ENTRYPOINT[ "java" , "-jar" ] # 启动应用固定命令
CMD["app.jar"] # 动态传递jar包名

参考链接

Docker容器 - DockerFile详解_不会调制解调的猫的博客-CSDN博客

Docker 进阶之 Dockerfile 详解

Docker进阶四-Dockerfile详解03_Coder-michael的博客-CSDN博客

Docker学习6 --- Dockerfile详解_lufei0920的博客-CSDN博客_dockerfile 获取当前路径

Dockerfile 详解_万wu皆可爱的博客-CSDN博客_dockerfile

dockerfile 详解 - 属于我的梦,明明还在 - 博客园

0 人点赞