深入DockerFile

深入DockerFile

一、概述

Dockerfile由一行行命令语句组成, 并且支持以 # 开头的注释行。 一般而言, Dockerfile 可以分为四部分基础镜像信息、维护者信息、镜像操作指令、启动时执行指令。

指令说明
FROM指定基础镜像
MAINTAINER指定维护者信息, 已经过时, 可以使用 LABEL maintainer=xxx 来替代
RUN运行命令v
CMD指定启动容器时默认的命令 v
ENTRYPOINT指定镜像的默认入口 . 运行命令 v
EXPOSE声明镜像内服务监听的端口 v
ENV指定环境变量, 可以在 docker run 的时候使用 -e 改变 v ; 会被固化到 i mage 的 config 里面
ADD复制指定的 src 路径下的内容到容器中的 dest 路径下, src 可以为 url 会自动下载, 可以为 tar 文件, 会自动解压
COPY复制本地主机的 src 路径下的内容到镜像中的 dest 路径下, 但不会自动解压等
LABEL指定生成镜像的元数据标签信息
VOLUME创建数据卷挂载点
USER指定运行容器时的用户名或 UID
WORKDIR配置工作目录, 为后续的 RUN 、 CMD 、 ENTRYPOINT 指令配置工作目录
ARG指定镜像内使用的参数(如版本号信息等) , 可以在 build 的时候, 使用 --build args 改变 v
OBBUILD配置当创建的镜像作为其他镜像的基础镜像是, 所指定的创建操作指令
STOPSIGNAL容器退出的信号值
HEALTHCHECK健康检查
SHELL指定使用 shell 时的默认 shell 类型
FROM  指定基础镜像

MAINTAINER  指定维护者信息,可以没有

RUN  你想让它干啥(在命令前面加上RUN即可)

ADD  添加宿主机的文件到容器内,还多了一个自动解压的功能
# RUN tar -Zxf /opt/xx.tgz 	# 报错!该tgz文件不存在! !

COPY  作用和ADD是一样的,都是拷贝宿主机的文件到容器内, COPY就是仅仅拷贝

WORKDIR  相当于cd命令,设置当前工作目录

VOLUME  设置目录映射,挂载主机目录

EXPOSE  指定对外的端口,在容器内暴露一个端口,端口 EXPORT 80

CMD  指定容器启动后的要干的事情

ENTRYPOINT	作用和CMD一样,都是在指定容器启动程序以及参数。
# 当指定了ENTRYPOINT之后,CMD指令的语义就有了变化,而是把CMD的内容当作参数传递给ENTRYPOINT指令。

ARG	 设置环境变量
# ARG只是用于构建镜像需要设置的变量,容器运行时就消失了

ENV   和ARG一样,都是设置环境变量
# 区别在于ENV无论是在镜像构建时,还是容器运行,该变量都可以使用

USER  用于改变环境,用于切换用户

二、常用指令

1、LABEL

标注镜像的一些说明信息。

LABEL multi.label1="value1 " multi.label2="value2" other="value3"
LABEL multi.label1="value1 " \ 
      multi.label2="value2" \ 
      other="value3"

2、RUN

RUN 指令在当前镜像层顶部的新层执行任何命令, 并提交结果, 生成新的镜像层。

生成的提交映像将用于 Dockerfile 中的下一步。 分层运行 RUN 指令并生成提交符合 Docker 的核心概 念, 就像源代码控制一样。

RUN:构建时期的指令
CMD:运行时期的指令

exec 形式可以避免破坏 shell 字符串, 并使用不包含指定 shell 可执行文件的基本映像运行 RUN 命令。 可以使用 SHELL 命令更改 shell 形式的默认 shell 。 在 shell 形式中, 您可以使用 \ (反斜杠) 将一条 RUN 指令继续到下一行。

RUN <command> ( shell 形式 , /bin/sh - c 的方式运行, 避免破坏 shell 字符串 ) 
RUN [ " executable", " param1 " , " param2" ] ( exec 形式 )

测试案例

[root@jacson ~/dockerfile]#vi Dockerfile 
FROM alpine
LABEL maintainer=leifengyang xx=aa
ENV msg=' hello atguigu itdachang'
RUN echo $msg
RUN ["echo","$msg"]    
RUN /bin/sh -c 'echo $msg' 
RUN ["/bin/sh","-c" ,"echo $msg"]       
CMD sleep 1

结果:

[root@jacson ~/dockerfile]#docker build --no-cache -t demo:v1.3 .
Sending build context to Docker daemon  2.048kB
Step 1/8 : FROM alpine
 ---> c059bfaa849c
Step 2/8 : LABEL maintainer=leifengyang xx=aa
 ---> Running in 5778621b2514
Removing intermediate container 5778621b2514
 ---> c946185cf348
Step 3/8 : ENV msg=' hello atguigu itdachang'
 ---> Running in 4966417054cc
Removing intermediate container 4966417054cc
 ---> 8eda0af71085
Step 4/8 : RUN echo $msg
 ---> Running in ca82ebaa1a59
hello atguigu itdachang
Removing intermediate container ca82ebaa1a59
 ---> 3d3ccffcae7b
Step 5/8 : RUN ["echo","$msg"]
 ---> Running in 0b1c3e8484c4
$msg
Removing intermediate container 0b1c3e8484c4
 ---> e0ed1c40af06
Step 6/8 : RUN /bin/sh -c 'echo $msg'
 ---> Running in 18365bcfffc7
hello atguigu itdachang
Removing intermediate container 18365bcfffc7
 ---> 4cacf20a152b
Step 7/8 : RUN ["/bin/sh","-c" ,"echo $msg"]
 ---> Running in 11bd0b3ee190
hello atguigu itdachang
Removing intermediate container 11bd0b3ee190
 ---> dd34682d9434
Step 8/8 : CMD sleep 1
 ---> Running in be4a71c9632b
Removing intermediate container be4a71c9632b
 ---> 0e3b241d6831
Successfully built 0e3b241d6831
Successfully tagged demo:v1.3

总结:

Step 5/8 : RUN ["echo","$msg"]
 ---> Running in 0b1c3e8484c4
$msg
结; 由于 [ ] 不是 shell 形式, 所以不能输出变量信息, 而是输出 $msg 。 其他任何 /bin/sh -c 的形式都 可以输出变量信息

什么是 shell 和 exec 形式 ?
1 . shell 是 /bin/sh -c 的方式。
2. exec [ " /bin/sh", " -c" , command] 的方式 == shell 方式 也就是 exec 默认方式不会进行变量替换。

3、CMD和ENTRYPOINT

都可以作为容器启动入口

CMD 的三种写法:

CMD ["executable","param1","param2"] ( exec 方式 , 首选方式 )
CMD ["param1","param2"] ( 为 ENTRYPOINT 提供默认参数 ) 
CMD command param1 param2 ( shell 形式 ) 

ENTRYPOINT 的两种写法:

ENTRYPOINT ["executable","param1","param2"] ( exec 方式 , 首选方式 ) 
ENTRYPOINT command param1 param2 (shell 形式 )

测试案例

FROM alpine
LABEL maintainer=jacson

CMD ["111"]
CMD ["222"]
ENTRYPOINT ["echo"]

结果

[root@jacson ~/dockerfile]#docker build -t demo:v1.3 -f Dock
[root@jacson ~/dockerfile]#docker run -it demo:v1.3 
222
#参数覆盖
[root@jacson ~/dockerfile]#docker run -it demo:v1.3 666
666

结论:
1 、只能有一个 CMD Dockerfile 中只能有一条 CMD 指令。 如果您列出多个 CMD , 则只有最后一个 CMD 才会生效。 CMD 的主要目的是为执行中的容器提供默认值。 这些默认值可以包含可执行文件, 也可以省略可 执行文件, 在这种情况下, 您还必须指定 ENTRYPOINT 指令。

2 、CMD 为 ENTRYPOINT 提供默认参数 如果使用 CMD 为 ENTRYPOINT 指令提供默认参数, 则 CMD 和 ENTRYPOINT 指令均应使用 JSON 数组格 式指定。

3、docker run 启动参数会覆盖 CMD 内容。

区别CMD和ENTRYPOINT

ENTRYPOINT作用和CMD一样,都是在指定容器启动程序以及参数。

当指定了ENTRYPOINT之后,CMD指令的语义就有了变化,而是把CMD的内容当作参数传递给ENTRYPOINT指令。

4、ARG和ENV

ARG声明的变量只在构建期有效,在运行期无效,只能在构建期修改

ENV声明的变量在构建期和运行期都生效,且在运行期可以修改

ARG:

ARG 指令定义了一个变量, 用户可以在构建时使用 --build-arg = 传递, docker build 命令会将其传递 给构建器。

–build-arg 指定参数会覆盖 Dockerfile 中指定的同名参数 。

如果用户指定了 未在 Dockerfile 中定义的构建参数 , 则构建会输出 警告 。

ARG 只在构建期有效, 运行期无效 。

不建议使用构建时变量来传递诸如 github 密钥, 用户凭据等机密。 因为构建时变量值使用 docker history 是可见的。

ARG 变量定义从 Dockerfile 中定义的行开始生效。 使用 ENV 指令定义的环境变量始终会覆盖同名的 ARG 指令。

ENV:
在构建阶段中所有后续指令的环境中使用, 并且在许多情况下也可以内联替换。

引号和反斜杠可用于在值中包含空格。

ENV MY_MSG hello 
ENV MY_NAME="John Doe" 
ENV MY_DOG=Rex\ The\ Dog 
ENV MY_CAT=fluffy 
# 多行写法如下 
ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
    MY_CAT=fluffy

测试案例:

FROM alpine
ARG arg1=11111
ENV arg2=22222
ENV runcmd=$arg1
RUN echo $arg1 $arg2 $runcmd
CMD echo $arg1 $arg2 $runcmd
[root@jacson ~/dockerfile]#docker build -t demo:v1.4 --build-arg arg1=333 -f Dockerfile3 .
Sending build context to Docker daemon  4.096kB
Step 1/6 : FROM alpine
 ---> c059bfaa849c
Step 2/6 : ARG arg1=11111
 ---> Running in e81ed34363d2
Removing intermediate container e81ed34363d2
 ---> 3c945615f4a6
Step 3/6 : ENV arg2=22222
 ---> Running in 3bb6442da3ae
Removing intermediate container 3bb6442da3ae
 ---> a2325c70f74d
Step 4/6 : ENV runcmd=$arg1
 ---> Running in 1a6c9039d224
Removing intermediate container 1a6c9039d224
 ---> 7c08f3545e00
Step 5/6 : RUN echo $arg1 $arg2 $runcmd
 ---> Running in 8b42632e046c
333 22222 333
Removing intermediate container 8b42632e046c
 ---> 4d8e10a1c2e1
Step 6/6 : CMD echo $arg1 $arg2 $runcmd
 ---> Running in a4638a31f065
Removing intermediate container a4638a31f065
 ---> 59abffeb767d
Successfully built 59abffeb767d
Successfully tagged demo:v1.4
[root@jacson ~/dockerfile]#docker run -it -e arg1=456 demo:v1.4 
456 22222 333

总结:
1、改变arg1的值,runcmd不会跟着改变
2、ENV 在 image 阶段就会被解析并持久化

5、ADD和COPY

COPY

COPY hom* /mydir/ # 当前上下文, 以 home 开始的所有资源 
COPY hom?.txt /mydir/ # ? 匹配单个字符 
COPY test.txt relativeDir/ # 目标路径如果设置为相对路径, 则相对与 WORKDIR 开始 # 把 “ test.txt” 添加到 <WORKDIR>/relativeDir/ 
COPY test.txt /absoluteDir/ # 也可以使用绝对路径, 复制到容器指定位置 

# 所有复制的新文件都是 uid(0)/gid(0) 的用户, 可以使用 --chown 改变 
COPY --chown=55:mygroup files* /somedir/ 
COPY --chown=bin files* /somedir/ 
COPY --chown=1 files* /somedir/ 
COPY --chown=10:11 files* /somedir/

ADD

同 COPY 用法, 不过 ADD 拥有自动下载远程文件和解压的功能。

注意:
src 路径必须在构建的上下文中; 不能使用 . . /something /something 这种方式, 因为 docker 构建的第一步是将上下文目录(和子目录)发送到 docker 守护程序。

如果 src 是 URL ,并且 dest 不以斜杠结尾, 则从 URL 下载文件并将其复制到 dest 。

如果 dest 以斜杠结尾, 将自动推断出 url 的名字(保留最后一部分,保存到 dest

如果 src 是目录, 则将复制目录的整个内容, 包括文件系统元数据。

6、WORKDIR 和 VOLUME

WORKDIR

WORKDIR 指令为 Dockerfile 中跟随它的所有 RUN , CMD , ENTRYPOINT , COPY , ADD 指令设置工作目 录。 如果 WORKDIR 不存在, 即使以后的 Dockerfile 指令中未使用它也将被创建。

WORKDIR 指令可在 Dockerfile 中多次使用。 如果提供了相对路径, 则它将相对于上一个 WORKDIR 指 令的路径。 例如:

WORKDIR /a 
WORKDIR b 
WORKDIR c 
RUN pwd 
# 结果 /a/b/c

也可以用到环境变量

ENV DIRPATH=/path 
WORKDIR $DIRPATH/$DIRNAME 
RUN pwd 
# 结果 /path/$DIRNAME

VOLUME

作用: 把容器的某些文件夹映射到主机外部 写法:

VOLUME ["/var/log/"] # 可以是 JSON 数组 
VOLUME /var/log # 可以直接写 
VOLUME /var/log /var/db # 可以空格分割多个

注意: 用 VOLUME 声明了卷, 那么以后对于卷内容的修改会被丢弃, 所以, 一定在 volume 声明之前修改内容.

7、USER

写法:

USER <user>[ :<group>] 
USER <UID>[ :<GID>]

USER 指令设置运行映像时要使用的用户名 (或 UID ) 以及可选的用户组(或 GID ) , 以及 Dockerfile 中 USER 后面所有 RUN , CMD 和 ENTRYPOINT 指令。

8、EXPOSE

EXPOSE 指令通知 Docker 容器在运行时在指定的网络端口上进行侦听。 可以指定端口是侦听 TCP 还 是 UDP , 如果未指定协议, 则默认值为 TCP 。

EXPOSE 指令实际上不会发布端口。 它充当构建映像的人员和运行容器的人员之间的一种文档, 即 有关打算发布哪些端口的信息。 要在运行容器时实际发布端口, 请在 docker run 上使用 -p 标志发布 并映射一个或多个端口, 或使用 - P 标志发布所有公开的端口并将其映射到高阶端口。

EXPOSE <port> [ <port>/<protocol>. . . ] 
EXPOSE [ 80, 443] 
EXPOSE 80/tcp 
EXPOSE 80/udp

9、multi-stage builds(多阶段构建)

案例

# 以下所有前提 保证 Dockerfile 和项目在同一个文件夹 
# 第一阶段: 环境构建 ; 用这个也可以 
FROM maven:3.5.0-jdk-8-alpine AS builder 
WORKDIR /app 
ADD . / /app 
RUN mvn clean package -Dmaven.test.skip=true 
# 第二阶段, 最小运行时环境, 只需要 jre ; 第二阶段并不会有第一阶段哪些没用的层 
# 基础镜像没有 j map ; jdk springboot-actutor ( jdk ) 
FROM openjdk:8-jre-alpine LABEL maintainer="534096094@qq.com"
# 从上一个阶段复制内容 
COPY --from=builder /app/target/*.jar /app.jar 
# 修改时区 
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo ' Asia/Shanghai' >/etc/timezone && touch /app.jar 
ENV JAVA_OPTS="" 
ENV PARAMS="" 
# 运行 j ar 包 
ENTRYPOINT ["sh","-c","java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar /app.jar $PARAMS" ]
##### 小细节 
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo ' Asia/Shanghai' >/etc/timezone 
或者 
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo ' Asia/Shanghai' >/etc/timezone 可以让镜像时间同步。 
## 容器同步系统时间 CST ( China Shanghai Timezone ) 
-v /etc/localtime:/etc/localtime:ro # 已经不同步的如何同步? 
docker cp /etc/localtime 容器 id : /etc/

docker build --build-arg url=“git address” -t demo:test . : 自动拉代码并构建镜像

FROM maven:3.6.1-jdk-8-alpine AS buildapp 
# 第二阶段, 把克隆到的项目源码拿过来 
# COPY --from=gitclone * /app/ 
WORKDIR /app COPY pom.xml . 
COPY src . 
RUN mvn clean package -Dmaven.test.skip=true 
# /app 下面有 target 
RUN pwd && ls -l 
RUN cp /app/target/*.jar /app.jar 
RUN ls -l 

### 以上第一阶段结束, 我们得到了一个 app.jar 
## 只要一个 JRE 
# FROM openjdk:8-jre-alpine 
FROM openjdk:8u282-slim 
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo ' Asia/Shanghai' >/etc/timezone LABEL maintainer="534096094@qq.com" 
# 把上一个阶段的东西复制过来 
COPY --from=buildapp /app.jar /app.jar 
# docker run -e JAVA_OPTS="-Xmx512m -Xms33 - " -e PARAMS="--spring.profiles=dev - -server.port=8080" - jar /app/app.jar 
# 启动 j ava 的命令
ENV JAVA_OPTS="" 
ENV PARAMS="" 
ENTRYPOINT ["sh","-c"," java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar /app.jar $PARAMS" ]

1 、自动从 git 下载指定的项目
2 、把项目自动打包生成镜像
3 、我们只需要运行镜像即可

学习链接:链接

Dockerfile实例

1、创建myfile文件
mkdir /myfile
2、在该文件夹中导入jdk.tar.gz
3、vim Dockerfile,拷贝粘贴(
FROM centos:7
MAINTAINER zzyy<zzyybs@126.com>
 
ENV MYPATH /usr/local
WORKDIR $MYPATH
 
#安装vim编辑器
RUN yum -y install vim
#安装ifconfig命令查看网络IP
RUN yum -y install net-tools
#安装java8及lib库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
#ADD 是相对路径jar,把jdk-8u171-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置
ADD jdk-8u171-linux-x64.tar.gz /usr/local/java/
#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_171
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
 
EXPOSE 80
 
CMD echo $MYPATH
CMD echo "success--------------ok"
CMD /bin/bash)

4、运行docker build -t centosjava8:1.5 .
5、运行docker run -it 容器id
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值