【云原生 | Docker篇】深刻Dockerfile(四)

2022年05月13日 阅读数:4
这篇文章主要向大家介绍【云原生 | Docker篇】深刻Dockerfile(四),主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。


【云原生 | Docker篇】深刻Dockerfile(四)_docker

文章目录php

​深刻Dockerfile​css

​前言​java

​1、命令说明​node

​2、FROM ​python

​3、LABEL​git

​4、RUN  ​github

​5、CMD和ENTRYPOINT​golang

​5.一、均可以做为容器启动入口​spring

​5.二、只能有一个CMD​docker

​5.三、CMD为ENTRYPOINT提供默认参数​

​5.四、组合最终效果​

​5.五、docker run启动参数会覆盖CMD内容 ​

​6、ARG和ENV​

​6.一、ARG​

​6.二、ENV​

​6.三、综合测试示例 ​

​7、ADD和COPY​

​7.一、COPY​

​7.二、ADD​

​8、WORKDIR和VOLUME​

​8.一、WORKDIR​

​8.二、VOLUME​

​9、USER​

​10、EXPOSE​

​11、multi-stage builds​

​11.一、使用​

​11.二、生产示例​

​12、Images瘦身实践 ​

​十3、springboot java 最终写法​


深刻Dockerfile

【云原生 | Docker篇】深刻Dockerfile(四)_docker_02

前言

博主语录:一文精讲一个知识点,多了你记不住,一句废话都没有

经典语录:一厢情愿,就得愿赌服輸

1、命令说明


Dockerfile 由一行行命令语句组成,而且支持以 # 开头的注释行。



通常而言,Dockerfile能够分为四部分



  • 基础镜像信息
  • 维护者信息
  • 镜像操做指令
  • 启动时执行指令 

指令

说明

FROM

指定基础镜像

MAINTAINER

指定维护者信息,已通过时,可使用LABELmaintainer-xxx来替代

RUN

运行命令v

CMD

指定启动容器时默认的命令v

ENTRYPOINT

指定镜像的默认入口.运行命令v

EXPOSE

声明镜像内服务监听的端口v

ENV

指定环境变量,能够在docker run的时候使用-e改变v;会被固化到image的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时的默认shel类型

 2、FROM 


FROM 指定基础镜像,最好挑一些 apline , slim 之类的基础小镜像


scratch 镜像是一个空镜像,经常使用于多阶段构建


如何肯定我须要什么要的基础镜像?


  • Java应用固然是java基础镜像(SpringBoot应用)或者Tomcat基础镜像(War应用)
  • JS模块化应用通常用nodejs基础镜像
  • 其余各类语言用本身的服务器或者基础环境镜像,如python、golang、java、php等

3、LABEL


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




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

4、RUN  

  • RUN指令在当前镜像层顶部的新层执行任何命令,并提交结果,生成新的镜像层。
  • 生成的提交映像将用于Dockerfile中的下一步。 分层运行RUN指令并生成提交符合Docker的核心概念,就像源代码控制同样。
  • exec形式能够避免破坏shell字符串,并使用不包含指定shell可执行文件的基本映像运行RUN命令。 可使用SHELL命令更改shell形式的默认shell。 在shell形式中,您可使用\(反斜杠)将一条RUN指令继续到下一行。




RUN <command> ( shell 形式 , /bin/sh - c 的方式运行,避免破坏 shell 字符串 )


RUN ["executable", "param1", "param2"] ( exec 形式 )





RUN /bin/bash -c 'source $HOME/.bashrc; \


echo $HOME'


# 上面等于下面这种写法


RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'


RUN ["/bin/bash", "-c", "echo hello"]



# 测试案例


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 10000





# 总结; 因为 [] 不是 shell 形式,因此不能输出变量信息,而是输出 $msg 。其余任何 /bin/sh -c 的形式均可以输出变量信息









总结:什么是shell和exec形式


5、CMD和ENTRYPOINT

5.一、均可以做为容器启动入口




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=leifengyang


CMD ["1111"]


CMD ["2222"]


ENTRYPOINT ["echo"]


# 构建出如上镜像后测试


docker run xxxx :效果 echo 1111




5.二、只能有一个CMD

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

5.三、CMD为ENTRYPOINT提供默认参数

  • 若是使用CMD为ENTRYPOINT指令提供默认参数,则CMD和ENTRYPOINT指令均应使用JSON数组格式指定。

5.四、组合最终效果

无ENTRYPOINT

ENTRYPOINTexec_entryp1_entry

ENTRYPOINT["exec_entry"pl_entry"]

无CMD

错误不容许的写法;容器没有启动命令

/bin/sh-cexec_entryp1_entry

exec_entry pl_entry

CMD[exec_cmd","pl_cmd"]

exec_cmd p1_cmd

/bin/sh-cexec_entryp1l_entry

exec_entry p1_entryexec_cmd p1_cmd

CMD["p1_cmd","p2_cmd"]

p1_cmd p2_cmd

/bin/sh-cexec_entrypl_entry

exec_entryp1_entry p1_cmdp2_cmd

CMDexec_cmdp1_cmd

/bin/sh-c exec_cmdp1_cmd

/bin/sh-cexec_entrypl_entry

exec_entry p1_entry/bin/sh-c exec_cmdp1_cmd

这条竖线,老是以ENTRYPOINT的为准

这条竖线,ENTRYPOINT和CMD共同做用

5.五、docker run启动参数会覆盖CMD内容 




# 一个示例


FROM alpine


LABEL maintainer=leifengyang


CMD ["1111"]


ENTRYPOINT ["echo"]


# 构建出如上镜像后测试


docker run xxxx :什么都不传则 echo 1111


docker run xxx arg1 :传入 arg1 则 echo arg1


6、ARG和ENV

6.一、ARG

  • ARG指令定义了一个变量,用户能够在构建时使用--build-arg = 传递,docker build命令会将其传递给构建器。
  • --build-arg 指定参数会覆盖Dockerfile 中指定的同名参数
  • 若是用户指定了 未在Dockerfile中定义的构建参数 ,则构建会输出 警告 。
  • ARG只在构建期有效,运行期无效
  • 不建议使用构建时变量来传递诸如github密钥,用户凭据等机密。由于构建时变量值使用docker history是可见的。
  • ARG变量定义从Dockerfile中定义的行开始生效。
  • 使用ENV指令定义的环境变量始终会覆盖同名的ARG指令。

6.二、ENV

  • 在构建阶段中全部后续指令的环境中使用,而且在许多状况下也能够内联替换。
  • 引号和反斜杠可用于在值中包含空格。
  • ENV 可使用key value的写法,可是这种不建议使用了,后续版本可能会删除




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


  • docker run --env 能够修改这些值
  • 容器运行时ENV值能够生效
  • ENV在image阶段就会被解析并持久化(docker inspect image查看),参照下面示例。


 FROM alpine


ENV arg=1111111


ENV runcmd=$arg


RUN echo $runcmd


CMD echo $runcmd


#ENV 的固化问题: 改变 arg ,会不会改变 echo 的值,会改变哪些值,如何修改这些值 ?


6.三、综合测试示例 




FROM alpine


ARG arg1=22222


ENV arg2=1111111


ENV runcmd=$arg1


RUN echo $arg1 $arg2 $runcmd


CMD echo $arg1 $arg2 $runcmd


7、ADD和COPY

7.一、COPY


COPY 的两种写法




COPY [--chown = <user>:<group>] <src>... <dest>


COPY [--chown = <user>:<group>] [ "<src>" ,... "<dest>" ]






  • --chown功能仅在用于构建Linux容器的Dockerfiles上受支持,而在Windows容器上不起做用
  • COPY指令从 src 复制新文件或目录,并将它们添加到容器的文件系统中,路径为 dest 。
  • 能够指定多个 src 资源,可是文件和目录的路径将被解释为相对于构建上下文的源。
  • 每一个 src 均可以包含通配符,而且匹配将使用Go的filepath.Match规则进行。



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/


7.二、ADD


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



注意:




  • src 路径必须在构建的上下文中; 不能使用 ../something /something 这种方式,由于docker构建的第一步是将上下文目录(和子目录)发送到docker守护程序。
  • 若是 src 是URL,而且 dest 不以斜杠结尾,则从URL下载文件并将其复制到 dest 。
  1. 若是 dest 以斜杠结尾,将自动推断出url的名字(保留最后一部分),保存到 dest
  • 若是 src 是目录,则将复制目录的整个内容,包括文件系统元数据。

8、WORKDIR和VOLUME

8.一、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


8.二、VOLUME


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




写法:






VOLUME ["/var/log/"] # 能够是 JSON 数组


VOLUME /var/log # 能够直接写


VOLUME /var/log /var/db # 能够空格分割多个






注意:


用 VOLUME 声明了卷,那么之后对于卷内容的修改会被丢弃,因此, 必定在 volume 声明以前修改内容 ;




9、USER


写法:





USER <user>[:<group>]


USER <UID>[:<GID>]





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




10、EXPOSE

  • EXPOSE指令通知Docker容器在运行时在指定的网络端口上进行侦听。 能够指定端口是侦听TCP仍是UDP,若是未指定协议,则默认值为TCP。
  • EXPOSE指令实际上不会发布端口。 它充当构建映像的人员和运行容器的人员之间的一种文档,即有关打算发布哪些端口的信息。 要在运行容器时实际发布端口,请在docker run上使用-p标志发布并映射一个或多个端口,或使用-P标志发布全部公开的端口并将其映射到高阶端口。




EXPOSE <port> [<port>/<protocol>...]


EXPOSE [80,443]


EXPOSE 80/tcp


EXPOSE 80/udp


11、multi-stage builds


多阶段构建



11.一、使用


https://docs.docker.com/develop/develop-images/multistage-build/




解决:如何让一个镜像变得更小 ; 多阶段构建的典型示例






### 咱们如何打包一个 Java 镜像


FROM maven


WORKDIR /app


COPY . .


RUN mvn clean package


COPY /app/target/*.jar /app/app.jar


ENTRYPOINT java -jar app.jar




## 这样的镜像有多大?


## 咱们最小作到多大?






11.二、生产示例


#  如下全部前提 保证 Dockerfile 和项目在同一个文件夹


# 第一阶段:环境构建 ; 用这个也能够


FROM maven:3.5.0-jdk-8-alpine AS builder


WORKDIR /app


ADD ./ /app


RUN mvn clean package -Dmaven.test.skip=true




# 第二阶段,最小运行时环境,只须要 jre ;第二阶段并不会有第一阶段哪些没用的层


#  基础镜像没有 jmap ; jdk springboot-actutor ( jdk )


FROM openjdk:8-jre-alpine


LABEL maintainer="lanson"




# 从上一个阶段复制内容


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=""




# 运行 jar 包


ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom


$JAVA_OPTS -jar /app.jar $PARAMS" ]





<!-- 为了加速下载须要在 pom 文件中复制以下 -->


<repositories>


<repository>


<id> aliyun </id>


<name> Nexus Snapshot Repository </name>


<url> https://maven.aliyun.com/repository/public </url>


<layout> default </layout>


<releases>


<enabled> true </enabled>


</releases>


<!--snapshots 默认是关闭的 , 须要开启 -->


<snapshots>


<enabled> true </enabled>


</snapshots>


</repository>


</repositories>


<pluginRepositories>


<pluginRepository>


<id> aliyun </id>


<name> Nexus Snapshot Repository </name>


<url> https://maven.aliyun.com/repository/public </url>


<layout> default </layout>


<releases>


<enabled> true </enabled>


</releases>



<snapshots>


<enabled> true </enabled>


</snapshots>


</pluginRepository>


</pluginRepositories>





###### 小细节


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 = "lanson"


# 把上一个阶段的东西复制过来


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


# 启动 java 的命令



ENV JAVA_OPTS = ""


ENV PARAMS = ""


ENTRYPOINT [ "sh" , "-c" , "java -Djava.security.egd=file:/dev/./urandom


$JAVA_OPTS -jar /app.jar $PARAMS " ]





本身 写一个多阶段构建


  • 一、自动从git下载指定的项目
  • 二、把项目自动打包生成镜像
  • 三、咱们只须要运行镜像便可

12、Images瘦身实践 

  • 选择最小的基础镜像
  • 合并RUN环节的全部指令,少生成一些层
  • RUN期间可能安装其余程序会生成临时缓存,要自行删除。如:




# 开发期间,逐层验证正确的


RUN xxx


RUN xxx


RUN aaa \


aaa \


vvv \




#  生产环境


RUN apt-get update && apt-get install -y \


bzr \


cvs \


git \


mercurial \


subversion \


&& rm -rf /var/lib/apt/lists/*


  • 使用 .dockerignore文件,排除上下文中无需参与构建的资源
  • 使用多阶段构建
  • 合理使用构建缓存加速构建。[--no-cache]

学习更多 Dockerfile 的写法: https://github.com/docker-library/




十3、springboot java 最终写法




FROM openjdk:8-jre-alpine


LABEL maintainer="lanson"


COPY 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=""


ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom


$JAVA_OPTS -jar /app.jar $PARAMS" ]


# 运行命令 docker run -e JAVA_OPTS="-Xmx512m -Xms33 -" -e PARAMS="--spring.profiles=dev --server.port=8080" -jar /app/app.jar



  • 欢迎点赞 收藏 留言 若有错误敬请指正!
  • 做者:Lansonli
  • 停下休息的时候不要忘了别人还在奔跑,但愿你们抓紧时间学习,全力奔赴更美好的生活