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"
注:一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
- 卷可以容器间共享和重用
- 容器并不一定要和其它容器共享卷
- 修改卷后会立即生效
- 对卷的修改不会对镜像产生影响
- 卷会一直存在,直到没有任何容器在使用它
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的参数
镜像制作分为两个阶段:
- docker build阶段 基于dockerfile制作镜像 (RUN,用于此阶段的运行命令)
- docker run阶段 基于镜像运行容器 (CMD,基于image run容器时候,需要运行的命令)
- 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 详解 - 属于我的梦,明明还在 - 博客园