深入了解Dockerfile(四)

1. 深入了解Dockerfile

文章目录

  • 1. 深入了解Dockerfile
    • 1. FROM
    • 2. LABEL
    • 3. RUN
    • 4. CMD和ENTRYPOINT
      • 1. 都可以作为容器启动入口
      • 2. 只能有一个CMD
      • 3. CMD为ENTRYPOINT提供默认参数
      • 4. 组合最终效果
      • 5. docker run启动参数会覆盖CMD内容
    • 5. ARG和ENV
      • 1. ARG
      • 2. ENV
      • 3. 综合测试示例
    • 6. ADD和COPY
      • 1. COPY
      • 2. ADD
    • 7. WORKDIR和VOLUME
      • 1. WORKDIR
      • 2. VOLUME
    • 8. USER
    • 9. EXPOSE
    • 10. multi-stage builds
      • 1. 使用
      • 2. 生产示例
    • 11. Images瘦身实践
    • 12. 实际测试Dockfile

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

基础的小linux系统。jdk;

一般而言,Dockerfile可以分为四部分

  • 基础镜像信息
  • 维护者信息
  • 镜像操作指令
  • 启动时执行指令
指令 说明
FROM 指定基础镜像
MAINTAINER 指定维护者信息,已经过时,可以使用LABEL maintainer=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时的默认shell类型

1. FROM

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

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

如何确定我需要什么要的基础镜像?

Java应用当然是java基础镜像(

  • SpringBoot应用)或者Tomcat基础镜像(War应用)

  • JS模块化应用一般用nodejs基础镜像

  • 其他各种语言用自己的服务器或者基础环境镜像,如python、golang、java、php等

2. LABEL

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

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

3. RUN

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

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

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

格式:
RUN  ( 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=jc xx=aa 
ENV msg='hello world' 
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形式

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

4. CMD和ENTRYPOINT

1. 都可以作为容器启动入口

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=jc
CMD ["1111"] 
CMD ["2222"] 
ENTRYPOINT ["echo"] 

#构建出如上镜像后测试 docker run xxxx:
效果 echo 1111

2. 只能有一个CMD

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

3. CMD为ENTRYPOINT提供默认参数

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

4. 组合最终效果

无ENTRYPOINT ENTRYPOINT exec_entry_p1_entry ENTRYPOINT [“exec_entry”, “p1_entry”]
无CMD 错误*,* 不允许的写 法 ;容器没有启动命令 /bin/sh -c exec_entry p1_entry exec_entry p1_entry
CMD [“exec_cmd”,“p1_cmd”] exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry exec_cmd p1_cmd
CMD [“p1_cmd”, “p2_cmd”] p1_cmd p2_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry p1_cmd p2_cmd
CMD exec_cmd p1_cmd /bin/sh -c exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd
这条竖线,总是以ENTRYPOINT的为准 这条竖线,ENTRYPOINT 和CMD共同作用

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

# 一个示例 
FROM alpine 
LABEL maintainer=jc 
CMD ["1111"] ENTRYPOINT ["echo"] 

#构建出如上镜像后测试 
docker run xxxx:什么都不传则 
echo 1111 docker run xxx arg1:传入arg1 则echo arg1

5. ARG和ENV

1. ARG

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

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

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

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

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

  • ARG变量定义从Dockerfile中定义的行开始生效。

  • 使用ENV指令定义的环境变量始终会覆盖同名的ARG指令。

2. 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的值,会改变哪些值,如何修改这些值?

3. 综合测试示例

FROM alpine 
ARG arg1=22222 
ENV arg2=1111111 
ENV runcmd=$arg1 
RUN echo $arg1 $arg2 $runcmd 
CMD echo $arg1 $arg2 $runcmd

6. ADD和COPY

1. COPY

COPY的两种写法

COPY [--chown=:] ...  
COPY [--chown=:] ["",... ""]
  • –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” 添加到 /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/

2. ADD

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

注意:

  • src 路径必须在构建的上下文中; 不能使用 …/something /something 这种方式,因为docker

    • 构建的第一步是将上下文目录(和子目录)发送到docker守护程序。
  • 如果 src 是URL,并且 dest 不以斜杠结尾,则从URL下载文件并将其复制到 dest 。

    • 如果 dest 以斜杠结尾,将自动推断出url的名字(保留最后一部分),保存到 dest
  • 如果 src 是目录,则将复制目录的整个内容,包括文件系统元数据。

7. WORKDIR和VOLUME

1. 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

2. VOLUME

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

写法

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

注意:

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

8. USER

写法:

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

9. EXPOSE

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

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

EXPOSE  [/...] 
EXPOSE [80,443] 
EXPOSE 80/tcp 
EXPOSE 80/udp

10. multi-stage builds

多阶段构建

1. 使用

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 

## 这样的镜像有多大? 
## 我们最小做到多大??

2. 生产示例

#以下所有前提 保证Dockerfile和项目在同一个文件夹 
# 第一阶段:环境构建; 
FROM maven:3.5.0-jdk-8-alpine AS builder WORKDIR /app ADD ./ /app 
RUN mvn clean package -Dmaven.test.skip=true 

# 第二阶段,最小运行时环境,只需要jre 
FROM openjdk:8-jre-alpine 
# 修改时区 
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone 
LABEL maintainer="[email protected]" 

# 从上一个阶段复制内容 
COPY --from=builder /app/target/*.jar /app.jar 
ENV JAVA_OPTS="" 
ENV PARAMS="" 

# 运行jar包 
ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar /app.jar $PARAMS" ]

 
	 
		aliyun 
		Nexus Snapshot Repository 
		https://maven.aliyun.com/repository/public 
		default 
		 
			true 
		 
		
		 
			true 
		 
	
	 
	
    	 
    		aliyun 
    		Nexus Snapshot Repository 
    		https://maven.aliyun.com/repository/public 
    		default 
    		 
    			true 
    		 
    		 
    			true 
    		 
    	 
    
######小细节 

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 git AS gitclone 
ARG url=http:// ARG appName=helloworld 
RUN git clone $url 
RUN cd $appName


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="[email protected]" 

# 把上一个阶段的东西复制过来 
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" ]

自己 写一个多阶段构建

1、自动从git下载指定的项目

2、把项目自动打包生成镜像

3、我们只需要运行镜像即可

11. Images瘦身实践

  • 选择最小的基础镜像

  • 合并RUN环节的所有指令,少生成一些层

  • RUN期间可能安装其他程序会生成临时缓存,要自行删除。如:

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/

12. 实际测试Dockfile

# 这是我的Dockerfile
FROM alpine

#给镜像加个标签
LABEL maintainer="leifengyang @ dd" \
abc=def \
aaa=bbb cccc=ddd   

#运行的指令,安装了软件,修改了文件,默认是用id=0 也就是root,这个基础系统的root用户
#代表镜像构建过程中运行的命令。
RUN echo 11111


#镜像启动如果要运行很长命令才行,容器启动执行的命令
##1、准备一个sh文件,让镜像启动运行sh文件(大多镜像操作)
##2、直接在CMD的位置写即可
CMD sleep 10;echo success


# 不可以引用多个
FROM alpine


LABEL maintainer="leifengyang @ dd" \
abc=def \
aaa=bbb cccc=ddd   


#指定构建参数【构建时】
ARG aaa=aaaa

#指定环境变量【为RUN以及CMD指定环境变量的】
ENV  parm=11111


# shell* 形式; bash -c "echo 11111"
RUN echo $parm  

#  exec 形式。$parm 默认拿不到ENV
RUN ["echo","$aaa"]

# 错误语法  RUN ["echo",'$parm']

# 错误语法  RUN ["echo",$parm]

# 错误语法。NOT FOUND(取不出环境变量【ENV】,ARG也是取不出)
#RUN ["echo",'${aaa}']

#RUN ["echo",${parm}]

#都是可以启动容器的命令有什么不同
#CMD sleep 1;echo $parm;echo $aaa;

# 都是可以启动容器的命令有什么不同
ENTRYPOINT sleep 1;echo $parm;



#可以在任意位置定义,并在以后取值使用,
#使用--build-arg version=3.13 改变;以我们传入的为准
ARG version=3.13.4
# 3.13  
FROM alpine:$version

LABEL maintainer="leifengyang" a=b \
c=dd

#构建期+运行期都可以生效;但是只能在运行期进行修改
#怎么修改:构建期修改和运行期修改
#构建期不能改 ENV的值
#运行期:docker run -e app=atguigu 就可以修改
ENV app=itdachang


##测试构建期间生效
RUN echo $app

RUN echo $param

# 定义以后的剩下环节(不包括运行时)能生效:取值$param;
#可以在构建时进行变化,docker build
# ARG不像ENV不能并排写
ARG param=123456  
ARG msg="hello docker"

#构建时期我们会运行的指令(根据Dockerfile创建一个镜像的整个过程时期)
RUN echo 11111

RUN echo $param
RUN echo $msg


#运行时期我们会运行的指令(根据之前创建的镜像启动一个容器,容器启动默认运行的命令)
#(docker run/docker start)
# CMD和ENTRYPOINT` 都是指定的运行时的指令

CMD ["/bin/sh","-c","echo 1111;echo $param;echo app_${app}"]


# env的坑
FROM alpine

# ARG msg=hello

# # ENV肯定能引用ARG
# ENV name=${msg}


# RUN echo ${name}

# RUN echo ${msg}
# ENV只能运行期改掉
ENV msg1=hello
ENV msg2=$msg1
# 以上构建期间就已经确定好值了;ENV持久化问题。

RUN echo ${msg1}
RUN echo ${msg2}

# msg1=msg2没问题;如果我运行期修改了msg1=66666的值,请求msg1;msg2输出什么
# 结果输出: 6666   hello;  传值不是传引用???原因:
# docker build的时候,env环境的信息会固化,直接在镜像配置里面就已经写死,msg1=hello,msg2=hello。
# -e 真的只能修改当前env本身
# 为什么运行期间能用ENV定义的所有值,一定是ENV存在某个地方
# 
CMD ["/bin/sh","-c","echo ${msg1};echo ${msg2};"]
#






# env的坑
FROM alpine


#把上下文Context指定的内容添加到镜像中,如果是压缩包,自动解压,
# 把当前内容复制到这个 alpine小系统里面
# 如果是远程文件,自动下载;
# 如果是压缩包,自动解压;
ADD https://download.redis.io/releases/redis-6.2.1.tar.gz  /dest/

#本地linux系统的内容文件添加进去  【宿主机   镜像内】
# docker build -t demo:test  -f Dockerfile 【.:上下文的文件路径】    : .代表上下文环境;代表Dockerfile所在的当前目录
#自动解压
# 压缩包位置:/root/dockerfiles
ADD *.tar.gz   /app/
# RUN ls -l

# 相当于给当前容器开一个用户,以后的命令可以用这个用户运行
# 不自动解压和下载
# COPY nginx 
# 以容器的用户:
# RUN  "useradd "
COPY  --chown=redis:redis   *.tar.gz  /redis/
# RUN指令上下并没有上下文关系;
# RUN cd /dest

# 当前还是列举的根目录
# RUN ls -l
RUN cd /dest && ls -l
RUN cd /app && ls -l
RUN cd /redis && ls -l
#把上下文Context指定的内容复制到镜像中
# COPY 





# env的坑
FROM alpine

# 开用户
#RUN adduser -u 1000 -g 1000
# 以后的所有命令会用 abc:abc 来执行。有可能没有执行权限
# 容器中的ROOT虽然不是linux宿主机的真实root,但是可以改掉这个镜像的所有



USER 1000:1000

# 把复制来的文件给用户所有权
COPY --chown=1000:1000   *.txt   /a.txt


RUN ls -l /

#不是root不能写
RUN  echo 2222 >> a.txt


# env的坑
FROM alpine

RUN pwd && ls -l

# 为以下所有的命令运行指定了基础目录
WORKDIR /app

# 可以为进入容器指定一个默认目录
WORKDIR abc

##比如我们的nginx镜像可以做成这样
#WORKDIR /usr/share/nginx/html

# /app/abc  多个WORKDIR可以嵌套
RUN pwd && ls -l

#复制到当前目录下
COPY *.txt   ./

RUN  pwd && ls -l

CMD ping baidu.com

# env的坑
FROM nginx
WORKDIR /usr/share/nginx/html


#剩下都是原来 nginx 默认的
FROM alpine

RUN mkdir /hello && mkdir /app
RUN echo 1111 > /hello/a.txt
RUN echo 222 > /app/b.txt
#挂载 容器的指定文件夹,如果不存在就创建。
#指定了 VOLUME ,即使启动容器没有指定 -v 参数,我们也会自动进行匿名卷挂载
# 容器内的 /hello ,/app 文件夹,请你在使用镜像启动容器的时候,自动给宿主机上挂载 

# VOLUME挂载出去的东西,容器改变也不会最终commit的时候生效
# -v 使用 VOLUME和-v挂载出去的目录(外面变,容器里面变)。但是
# 所有改变也生效了,1)、但是 docker commit 提交当前容器的所有变化为镜像的时候,就会丢弃
# 2)、VOLUME [ "/hello","/app" ] 容器以后自动挂载,在Dockerfile中对VOLUME的所有修改都不生效
# 3)、挂载只有一点就是方便在外面修改,或者把外面的东西直接拿过来
# 所以这个写在最后
# JAVA 日志都要挂外面 /app/log
# VOLUME ["/log"]

VOLUME [ "/hello","/app" ]
# VOLUME 指定的挂载目录

# 这两句话没有生效
RUN echo 6666 >> /hello/a.txt
RUN echo 8888 >> /app/b.txt

RUN cd /hello && echo 88888 >>a.txt


#暴露 ,这个只是一个声明;给程序员看。docker也能看到
# docker -d -P(随机分配端口,)
EXPOSE 8080
EXPOSE 999

# 
CMD ping baidu.com

FROM alpine



# ENTRYPOINT: 入口(真正的门)
# ENTRYPOINT [ "ping" ]

# 命令(进门的时候带口令)
# 最终的用法: CMD是给ENTRYPOINT提供参数的

#CMD可以被修改
# CMD ping baidu.com


# ENTRYPOINT + CMD = 容器的完整启动命令

# 这是启动命令
# ENTRYPOINT ping + CMD baidu.com = 错误

#多个CMD只有最后一次生效
# CMD ping baidu.com


# ["echo","${param}"] 不是bash -c的方式,取不出环境变量性  【】
# echo $param     = ["/bin/sh","-c","多长的命令都写在这里  echo ${param}"]

# ENTRYPOINT或者CMD作为唯一入口,只能写一个,最后一个生效
# ENTRYPOINT ping atguigu.com

# RUN,CMD,ENTRYPOINT
# []:  ["/bin/sh","-c"] = shell
# shell:

FROM alpine
ENV url=baidu.com

#CMD ["ping","baidu.com"]

# CMD ["useradd","-u","1000","-g","2000"]

# CMD ["ping","${url}"]  取不出变量

# CMD ping ${url}


# 官方都是建议使用 []方式
# CMD ["/bin/sh","-c","ping ${url}"]



# ENTRYPOINT ping baidu.com + CMD怎么写都没用,容器启动都是以ENTRYPOINT的完整命令为准


# java -jar xxxx.jar --spring.profile=dev --server.port=8888


# 这两个合在一起不能是错误的命令

#官方推荐的写法,,变化的写CMD,而CMD是提供参数给ENTRYPOINT
# docker run imageName  cmd1  一旦传递了cmd1,CMD指定的所有参数都会被覆盖,
# 自定义参数的情况下一定要传完
CMD [ "5","baidu.com" ]

#exec的写法 不变的写 ENTRYPOINT;未来他是容器启动的唯一入口,
ENTRYPOINT [ "ping","-c" ]

FROM alpine

RUN  安装maven
RUN mvn clean package

COPY  xx.jar /app.jar

ENTRYPOINT [ "java","-jar","app.jar" ]


#SpringBoot应用 java -jar xxx.jar

# jre环境;可以自己打包
# 一个镜像分为多个大的阶段进行构建,最终的构建结果是最后一个阶段的结果



# 多阶段构建
# FROM alpine AS build
# xxxxxx



# FROM  jre

# COPY  --from=build xxx  xxx

# ENTRYPOINT [ "executable" ]

FROM  maven:3.6.1-jdk-8-alpine AS buildapp

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="[email protected]"
# 把上一个阶段的东西复制过来
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" ]

你可能感兴趣的:(Docker,docker)