栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Docker学习笔记 —— Dockerfile

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Docker学习笔记 —— Dockerfile

系列文章目录

Docker学习笔记 —— Docker 介绍
Docker学习笔记 —— Docker 安装
Docker学习笔记 —— Docker 常用命令
Docker学习笔记 —— Docker 存储
Docker学习笔记 —— Docker 网络
Docker学习笔记 —— Dockerfile


文章目录
  • 系列文章目录
  • 一、 docker build
  • 二、环境置换(Environment replacement)
  • 三、 dockerfile 指令介绍
    • 3.1 FROM
    • 3.2 LABEL
    • 3.3 MAINTAINER (已弃用)
    • 3.4 RUN
    • 3.5 CMD / ENTRYPOINT
      • 3.5.1 CMD
      • 3.5.2 ENTRYPOINT
      • 3.5.3 CMD、ENTRYPOINT关系
    • 3.6 ARG / ENV
      • 3.6.1 ARG
      • 3.6.2 ENV
    • 3.7 ADD / COPY
      • 3.7.1 ADD
      • 3.7.2 COPY
    • 3.8 WORKDIR / VOLUME
      • 3.8.1 WORKDIR
      • 3.8.2 VOLUME
    • 3.9 USER
    • 3.10 EXPOSE
  • 四、多阶段构建
    • 4.1 Images瘦身


  • Docker 可以通过读取Docker文件中的说明自动生成镜像。
  • Docker 是一个文本文档,其中包含用户可以在命令行上调用以组装镜像的所有命令,用户可以使用docker build创建一个连续执行多个命令行执行的自动构建;
一、 docker build
  • docker build 命令从Dockerfile和上下文构建镜像,生成的上下文是可以指定为本地文件系统上的目录,还可以指定为Git存储库位置。构建上下文是递归处理的;

  • 构建是由Docker守护进程运行,而不是由CLI运行;

    • 构建过程要做的第一件事是(递归处理)将整个上下文发送到守护进程,大多数情况下,最该从一个空目录作为上下文开始,并将Dockerfile 保存在该目录中,不要使用根目录 / 作为 PATH 构建上下文,因为它会导致构建将硬盘驱动器的全部内容传输到Docker守护进程;
    • 传统上,Dockerfile位于上下文的根目录中,也可以通过在 docker build中使用-f 来指向文件系统中任何位置的Dockerfile,还可以通过 -t来制定存储库和tag;
二、环境置换(Environment replacement)
  • 环境变量(用ENV语句声明)也可以在某些指令中用作由 Dockerfile 解释的变量;

  • 环境变量在Dockerfile中用$variable_name 或 ${variable_name} 表示,大括号的语法通常用于解决变量名没有空格的问题如${variable}_name;

  • ${variable_name}还支持一下标准Bash修饰符:

    • ${variable:-word},表示如果设置了变量,则结果将是该值,如果未设置,则结果将为 word;
    • ${variable:+word},表示如果设置了变量,则机构将是该值,如果未设置,则结果将是空字符串;
三、 dockerfile 指令介绍 3.1 FROM
  • 格式:

    • FROM [--platform=] [AS ]
    • FROM [--platform=] [:] [AS ]
    • FROM [--platform=] [@] [AS ]
  • FROM 指令初始化一个新的构建阶段并为后续指令设置基础镜像。因此,有效的Dockerfile 必须以FROM指令开头(ARG可以先于FROM),镜像可以是任何有效的镜像,最好挑一些apline,slim之类的基础小镜像,scratch镜像是一个空镜像,常用于多阶段构建;

  • FROM可以在单个Dockerfile中多次出现,以创建多个镜像或将一个构建阶段用作另一个构建阶段的依赖项。只需要在每条新FROM指令之前几下commit输出的最后一个镜像ID。每个FROM指令都会清除以前的指令创建的任何状态;

  • 可以通过添加 AS name 到 FROM指令来为新的构建阶段命名,这个名称可在后续FROM和COPY --from= 说明中用于引用此阶段构建的镜像;

  • tag 和 digest 值是可选的,如果省略不写,默认使用 lastest ,如果找不到,将返回错误;

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

    • Java应用当然是java基础镜像(SpringBoot应用)或者T omcat基础镜像(War应用)
    • JS模块化应用一般用nodejs基础镜像
    • 其他各种语言用自己的服务器或者基础环境镜像,如python、golang、java、php等
ARG version=3.13

#不可以引用多个
FROM alpine:$version

CMD ["/bin/sh","-c","echo 11111"]

3.2 LABEL
  • 格式:

    • LABEL = = = ...
  • 键值对结构,该指令向镜像添加元数据,标注镜像的一些说明信息,LABEL值中包含空格可以使用引号、 来处理;

LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates 
that label-values can span multiple lines."
3.3 MAINTAINER (已弃用)
  • 格式:

    • MAINTAINER
  • MAINTAINER指令设置生成图像的作者字段,已弃用,用 LABEL 代替;

3.4 RUN
  • 格式:

    • RUN

      • shell形式,命令在 shell 中运行,默认/bin/sh -c在 Linux 或cmd /S /CWindows 上;
      • shell形式,/bin/sh -c 的方式;
    • RUN ["executable", "param1", "param2"]

      • exec形式,不调用命令shell,不会进行shell处理

      • 例如:RUN[“echo”,"$HOME"]不会对变量 $HOME进行替换,如果想要shell处理,那么:

        • 使用shell形式;
        • 要么直接执行shell,例如 RUN["sh","-c","echo $HOME"];
  • RUN命令将在当前图层之上的新层中执行任何命令并提交结果,生成的提交镜像将用于 Dockerfile的下一步;

  • 分层运行 RUN 指令并生成提交符合Docker的核心概念,在Docker中,提交很便宜,可以源代码管理一样从镜像历史中的任何点创建容器;

  • exec形式可以避免破坏shell字符串,并使用不包含指定shell可执行文件的基本映像运行RUN命令。

  • 可以使用SHELL命令更改shell形式的默认shell。 在shell形式中,您可以使用(反斜杠)将一条 RUN指令继续到下一行;

#指定构建参数【构建时】
ARG version=3.13

#不可以引用多个
FROM alpine:$version

#给镜像加元数据信息 
LABEL maintainer=dozezz date=20210924

#指定构建参数【构建时】
ARG dozezz=dozezz 
ARG doze=doze

#指定环境变量【为RUN以及CMD指定环境变量时】
ENV msg="hello dockerfile !"

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

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

# shell* 形式; bash -c "echo 11111"
RUN /bin/sh -c "echo $msg"

#  exec 形式。$msg 默认拿不到ENV
RUN ["/bin/sh","-c","echo $msg"]

CMD ["/bin/sh","-c","sleep 1000;echo $dozezz;echo $msg;"]
3.5 CMD / ENTRYPOINT 3.5.1 CMD
  • 有三种形式:

    • CMD ["executable","param1","param2"](EXEC形式,这是首选形式);
    • CMD ["param1","param2"](作为ENTRYPOINT 的默认参数);
    • CMD command param1 param2(SHELL 形式);
  • Doeckerfile 中只能有一个CMD指令,如果列出多个命令,则只有最后一个生效;

  • CMD的主要用途是为正在执行的容器踢动默认值,这些默认值可以包括可执行文件,也可以省略可执行文件,但是必须指 ENTRYPOINT;

  • CMD在当做 ENTRYPOINT 指令提供默认参数时候,应该使用JSON数组格式指定CMD和ENTRYPOINT指令,这意味着参数等使用双引号,而不是单引号;

  • *exec*形式不调用命令 shell。这意味着不会发生正常的 shell 处理。例如:

    • CMD [ "echo", "$HOME" ]不会对 进行变量替换$HOME。如果你想要 shell 处理,那么要么使用shell形式,要么直接执行 shell,例如:CMD [ "sh", "-c", "echo $HOME" ]
FROM ubuntu
CMD echo "This is a test." | wc -
FROM
CMD ["/usr/bin/wc","--help"]
3.5.2 ENTRYPOINT
  • 有两种形式:

    • ENTRYPOINT ["executable", "param1", "param2"](EXEC形式,这是首选形式);

      • docker run -d 将-d参数传递给入口点。您可以ENTRYPOINT使用docker run --entrypoint 标志覆盖指令;
    • ENTRYPOINT command param1 param2(SHELL 形式);

      • 这种形式将使用 shell 处理来替换 shell 环境变量,并且将忽略任何CMD或docker run命令行参数;
3.5.3 CMD、ENTRYPOINT关系
无ENTRYPOINTENTRYPOINT exec_entry p1_entryENTRYPOINT [“exec_entry” ,“p1_entry”]
无CMD错误, 不允许的写法/bin/sh -c exec_entry p1_entryexec_entry p1_entry
CMD[“exec_cmd”,“p1_cmd”]exec_cmd p1_cmd/bin/sh -c exec_entry p1_entryexec_entry p1_entry
exec_cmd p1_cmd
CMD[“p1_cmd” ,“p2_cmd”]p1_cmd p2_cmd/bin/sh -c exec_entry p1_entryexec_entry p1_entry p1_cmd p2_cmd
3.6 ARG / ENV 3.6.1 ARG
  • 格式:

    • ARG [=]
  • ARG指令定义了一个变量,用户可以在构建时使用–build-arg = 传递,docker build命令会将其传递给构建器,–build-arg 指定参数会覆盖Dockerfile 中指定的同名参数;

  • 如果用户指定了未在 Dockerfile 中定义的构建参数,则构建会输出警告,ARG只在构建期有效,运行期无效;

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

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

3.6.2 ENV
  • 格式:

    • ENV = ...
  • ENV指令将环境变量设置为 ;

  • 此值将在构建阶段的所有后续指令的环境中,并且也可以在许多中内联替换。

  • 与命令行解析一样,引号和反斜杠可用于在值中包含空格。

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

#不可以引用多个
FROM alpine:$version

#给镜像加元数据信息 
LABEL maintainer=dozezz date=20210924

#指定构建参数【构建时】
#ARG不像ENV不能并排写
ARG dozezz=dozezz 
ARG doze=doze

#指定环境变量【为RUN以及CMD指定环境变量时】
#构建期+运行期都可以生效;但是只能在运行期加参数 -e 修改(docker run -e msg=aaa ), 构建期不能修改 ENV的值
ENV msg="hello dockerfile !"

#构建时期我们会运行的指令(根据Dockerfile创建一个镜像的整个过程时期)
# shell* 形式; bash -c "echo 11111"
RUN echo $dozezz
RUN echo $msg

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

# 错误语法
#RUN ["/bin/bash","-c","$msg"]
#RUN ["/bin/bash","-c","$dozezz"]

#运行时期我们会运行的指令(根据之前创建的镜像启动一个容器,容器启动默认运行的命令)
CMD ["/bin/sh","-c","echo $dozezz;echo $msg;"]

3.7 ADD / COPY 3.7.1 ADD
  • 格式:

    • ADD [--chown=:] ...
    • ADD [--chown=:] ["",... ""]
    • 该--chown功能仅在用于构建 Linux 容器的 Dockerfile 上受支持,不适用于 Windows 容器;
  • ADD指令从中复制新文件、目录或远程文件 URL ,并将它们添加到路径 处的图像文件系统中;

  • 可以指定多个资源,但如果它们是文件或目录,则它们的路径被解释为相对于构建上下文的源;

  • 每个都可能包含通配符,匹配将使用 Go 的 filepath.Match规则完成;

  • 是一个绝对路径,或相对于WORKDIR的路径,源文件将被复制到目标容器中。;

  • ADD遵守以下规则:

    • 路径必须是内部语境的构建;

      • 你不能ADD ../something /something,因为docker构建的第一步 docker build是将上下文目录(和子目录)发送到 docker 守护进程;
    • 如果是 URL 并且不以斜杠结尾,则从 URL 下载文件并将其复制到;

    • 如果是 URL 并且确实以斜杠结尾,则从 URL 推断文件名并将文件下载到 /;

      • 例如,ADD http://example.com/foobar /将创建文件/foobar;
      • http://example.com 将不起作用;
    • 如果是目录,则复制目录的全部内容,包括文件系统元数据(不会复制目录本身,只会复制其内容);

    • 如果是可识别的压缩格式(identity、gzip、bzip2 或 xz)的本地tar 存档,则将其解压缩为目录。来自远程URL 的资源不会被解压缩;

    • 如果是任何其他类型的文件,则将其与其元数据一起单独复制。在这种情况下,如果以斜杠结尾/,它将被视为一个目录,其内容将被写入/base();

    • 如果直接指定了多个资源,或者由于使用了通配符,则必须是目录,并且必须以斜杠结尾/;

    • 如果不以斜杠结尾,则将其视为常规文件,并将其内容写入

    • 如果不存在,则会将其与路径中所有缺失的目录一起创建;

3.7.2 COPY
  • 格式:

    • COPY [--chown=:] ...
    • COPY [--chown=:] ["",... ""]
  • COPY指令从中复制新文件或目录 并将它们添加到容器的文件系统中的路径;

  • 可以指定多个资源,但文件和目录的路径将被解释为相对于构建上下文的源;

  • 每个都可能包含通配符,匹配将使用 Go 的 filepath.Match规则完成;

  • 是一个绝对路径,或相对于WORKDIR的路径,源文件将被复制到目标容器中。

  • COPY接受一个标志--from=,该标志可用于将源位置设置为上一个生成阶段(使用from..AS创建),该生成阶段将代替用户发送的生成上下文。如果找不到具有指定名称的生成阶段,将尝试使用具有相同名称的镜像。

  • COPY 遵守以下规则:

    • 路径必须是内部语境的构建;

      • 不能 copy ../something/something,因为docker构建的第一步是将上下文目录(和子目录)发送到docker守护进程;
    • 如果是目录,则复制目录的全部内容,包括文件系统元数据(不会复制目录本身,只会复制其内容);

    • 如果是任何其他类型的文件,则将其与其元数据一起单独复制。在这种情况下,如果以斜杠结尾/,它将被视为一个目录,其内容将被写入/base();

    • 如果直接指定了多个资源,或者由于使用了通配符,则必须是目录,并且必须以斜杠结尾/;

    • 如果不以斜杠结尾,则将其视为常规文件,并将其内容写入

    • 如果不存在,则创建它及其路径中所有丢失的目录;

FROM alpine

#把上下文Context指定的内容添加到镜像中
# 1.如果是远程文件,自动下载;
ADD https://download.redis.io/releases/redis-6.0.0.tar.gz /dest/

RUN cd /dest/
RUN cd /dest && ls -l

# 2.本地linux系统docker上下文的内容文件添加镜像中,如果是压缩包,自动解压; 
# docker build -t demo:test  -f Dockerfile 【.:上下文的文件路径】    : .代表上下文环境;代表Dockerfile所在的当前目录
ADD *.tar.gz   /app/

# RUN指令上下并没有上下文关系;例如:第一个cd a/ ,第二个 cd b/ 不会进入 a/b/
RUN cd /app/
RUN cd /app && ls -l

# RUN指令上下并没有上下文关系;
RUN cd /dest
RUN ls -l
RUN cd /app
RUN ls -l


# 开用户
#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 

CMD sleep 20

3.8 WORKDIR / VOLUME 3.8.1 WORKDIR
  • 格式:

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

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

FROM alpine

RUN pwd && ls -l

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

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

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

# docker exec -it时候会进入 WORKDIR,比如我们的nginx镜像可以做成这样
#WORKDIR /usr/share/nginx/html

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

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

RUN  pwd && ls -l

CMD ping baidu.com

3.8.2 VOLUME
  • 格式:

    • VOLUME ["/data"]
  • VOLUME指令创建一个具有指定名称的挂载点,并将其标记为保存来自本机主机或其他容器的外部挂载卷;

  • 该值可以是 JSON 数组、VOLUME ["/var/log/"]或带有多个参数的纯字符串,例如

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

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

# VOLUME 指定的挂载目录
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

3.9 USER
  • 格式

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

3.10 EXPOSE
  • 格式:

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

  • EXPOSE指令实际上不会发布端口。 它充当构建映像的人员和运行容器的人员之间的一种文档,即有关打算发布哪些端口的信息。

  • 要在运行容器时实际发布端口,请在docker run上使用-p标志发布并映射一个或多个端口,或使用-P标志发布所有公开的端口并将其映射到高阶端口。

  • 遇到个坑,dockerfile中没有 EXPOSE,在docker run时 -P没有端口暴露,加上就好了的坑;

四、多阶段构建
FROM maven:alpine AS builderapp

#设置工作目录
WORKDIR /app

#拷贝宿主机文件到镜像中
COPY ./ ./

#RUN pwd && ls -l
#RUN cd /app/src&&ls -l

#编译文件,跳过测试
RUN mvn clean package -Dmaven.test.skip=true

#RUN cd /app/target/ && ls -l
#RUN cp /app/target/*.jar /app/app.jar

#RUN pwd&&ls -l

#ENTRYPOINT java -jar app.jar

#EXPOSE 8080

FROM openjdk:8-jre-alpine

LABEL maintainer=dozezz

# 上个阶段复制内容
COPY --from=builderapp /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" ]
4.1 Images瘦身
  • 选择最小的基础镜像
  • 合并RUN环节的所有指令,少生成一些层
  • RUN期间可能安装其他程序会生成临时缓存,要自行删除。如:
  • 使用 .dockerignore 文件 ,排除上下文中无需参与构建的资源
  • 使用多阶段构建
  • 合理使用构建缓存加速构建。[–no-cache]
  • 学习更多Dockerfile的写法:https://github.com/docker-library/
# 开发期间,逐层验证正确的
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/*
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/276984.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号