阅读目录

Dockerfile是一个文本文件,包含一些Docker指令。执行docker build,Docker就会执行Dockerfile里面的指令,来自动创建镜像。

用法

Dockerfile里面的指令可以访问context这些文件。

context是递归的,PATH包含所有子目录,URL包含所有子模块。

例子,把当前目录当做context,

$ docker build .

Sending build context to Docker daemon  6.51 MB
...

build是由Docker daemon(守护进程)来运行,而不是CLI。

build会把整个context发给daemon。所以最好把context设置为空目录,把Dockerfile放进去。只添加需要的文件,为了提高build性能,还可以添加.dockerignore来排除一些文件和目录。

Warning!不要用系统根目录/作为PATH,不然会把根目录下所有东西都传给Docker daemon。

一般会把Dockerfile放在context根目录下,也可以使用-f来指定其他路径,

$ docker build -f /path/to/a/Dockerfile .

指定镜像存放仓库可以使用-t

$ docker build -t shykes/myapp .

支持多个,

$ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .

Docker daemon在执行Dockfile的指令前,会做检查,如果有语法错误会报错,

$ docker build -t test/myapp .

Sending build context to Docker daemon 2.048 kB
Error response from daemon: Unknown instruction: RUNCMD

Docker daemon执行指令,是一个一个执行,一个一个提交的。执行结束会生成镜像ID。自动清理context。

RUN cd /tmp是无效的,因为daemon是独立执行每条指令的,不会作用到后面的指令。

为了加速build过程,Docker会重复使用中间镜像(缓存),在console日志中可以看到Using cache

$ 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来源于之前本地build过的镜像,或者使用docker load加载的镜像。

如果想直接指定一个镜像作为cache,可以使用--cache-from

格式

# Comment
INSTRUCTION arguments

#开头是注释或者parser directive(提示解析器做特殊处理)。

指令是忽略大小写的,不过为了和参数区分,一般全大写。

Dockerfile从上往下顺序执行指令,第一条指令必须是FROM,定义build的parent image(父镜像)。没有parent的镜像叫base image。

参数里面的#就不是注释了,是参数的一部分,

# Comment
RUN echo 'we are running some # of cool things'

注释在Dockerfile指令执行前,会被移除。以下是等价的,

RUN echo hello \
# comment
world
RUN echo hello \
world

注意,注释不支持换行符\

注释和指令前面的空格会被忽略,以下是等价的,

        # this is a comment-line
RUN echo hello
RUN echo world
# this is a comment-line
RUN echo hello
RUN echo world

但是参数里面的空格,是会被保留的,

RUN echo "\
hello\
world"

Parser directives

# directive=value

Parser directives是一种特殊的注释,用来提示解析器做特殊处理。

但是Parser directives并不会添加layers到build中,也不会被识别为build step。

如果注释、空行、或者指令被运行后,Docker就不会再识别Parser directives了,所以必须把Parser directives放在Dockerfile的最前面的最前面。

Parser directives是忽略大小写的,不过一般约定为全小写。同时约定随后跟一个空行。

Parser directives不支持换行符。

以下是一些无效示例,

无效--换行符

# direc \
tive=value

无效--出现了2次

# directive=value1
# directive=value2 FROM ImageName

无效--在指令之后就是普通的注释

FROM ImageName
# directive=value

无效--在普通注释之后也变成了普通注释

# About my dockerfile
# directive=value
FROM ImageName

无效--未知命令会被视为普通注释,普通注释之后也是普通注释

# unknowndirective=value
# knowndirective=value

Parser directives同一行的空格会被忽略,以下是等价的,

#directive=value
# directive =value
# directive= value
# directive = value
# dIrEcTiVe=value

目前支持2个Parser directives,

  • syntax,依赖BuildKit
  • escape

escape

反斜杠(默认)

# escape=\

或者反引号

# escape=`

用来指定转义符。这个在Windows系统很有用,因为\在Windows是路径分隔符。

比如,

FROM microsoft/nanoserver
COPY testfile.txt c:\\
RUN dir c:\

会执行失败,

PS C:\John> docker build -t cmd .
Sending build context to Docker daemon 3.072 kB
Step 1/2 : FROM microsoft/nanoserver
---> 22738ff49c6d
Step 2/2 : COPY testfile.txt c:\RUN dir c:
GetFileAttributesEx c:RUN: The system cannot find the file specified.
PS C:\John>

使用escape可以替换\为`

# escape=`

FROM microsoft/nanoserver
COPY testfile.txt c:\
RUN dir c:\

执行成功,

PS C:\John> docker build -t succeeds --no-cache=true .
Sending build context to Docker daemon 3.072 kB
Step 1/3 : FROM microsoft/nanoserver
---> 22738ff49c6d
Step 2/3 : COPY testfile.txt c:\
---> 96655de338de
Removing intermediate container 4db9acbb1682
Step 3/3 : RUN dir c:\
---> Running in a2c157f842f5
Volume in drive C has no label.
Volume Serial Number is 7E6D-E0F7 Directory of c:\ 10/05/2016 05:04 PM 1,894 License.txt
10/05/2016 02:22 PM <DIR> Program Files
10/05/2016 02:14 PM <DIR> Program Files (x86)
10/28/2016 11:18 AM 62 testfile.txt
10/28/2016 11:20 AM <DIR> Users
10/28/2016 11:20 AM <DIR> Windows
2 File(s) 1,956 bytes
4 Dir(s) 21,259,096,064 bytes free
---> 01c7f3bef04f
Removing intermediate container a2c157f842f5
Successfully built 01c7f3bef04f
PS C:\John>

环境替换

环境变量(使用ENV指令来定义环境变量)能够用在指令中作为变量,被Dockerfile解释。还可以处理转义符,以便在语句中照字面值地包含variable-like语法。

使用$variable_name${variable_name}来引用环境变量。

可以使用双括弧和下划线来命名,如${foo}_bar。同时支持bash修饰符,

  • ${variable:-word} set variable后就是set的值,没有set variable值就是word
  • ${variable:+word} set variable后值就是word,没有set variable就是空字符串

word既可以是string,也可以是另外一个环境变量。

可以在变量前加转义符,比如\$foo\${foo}会被分别转义为$foo${foo}

示例,

FROM busybox
ENV foo /bar
WORKDIR ${foo} # WORKDIR /bar
ADD . $foo # ADD . /bar
COPY \$foo /quux # COPY $foo /quux

Dockerfile的一下指令都支持环境变量

  • ADD
  • COPY
  • ENV
  • EXPOSE
  • FROM
  • LABEL
  • STOPSIGNAL
  • USER
  • VOLUME
  • WORKDIR
  • ONBUILD (结合以上指令使用)

需要注意的是,变量替换是针对整条指令的,

ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc

def的值是hello,而不是bye,因为上一条指令赋值的hello。

ghi的值才会是bye。

.dockerignore file

.dockerignore文件位于context根目录,会把匹配到的文件和目录排除在context之外。

这样就可以在使用ADDCOPY命令时,避免把一些大文件或者敏感信息文件和目录,发送到Docker daemon。

context是由PATHURL定义的,所以.dockerignore文件会匹配这2个路径。

/foo/bar == foo/bar

示例,

# comment
*/temp*
*/*/temp*
temp?
Rule Behavior
# comment 注释忽略
*/temp* 排除root的子目录下,temp开头的文件和目录。
/somedir/temporary.txt/somedir/temp
*/*/temp* 排除root的二层目录下,temp开头的文件和目录。
/somedir/subdir/temporary.txt
temp? 排除root下, temp+1个字符的文件和目录。
/tempa/tempb

匹配遵循Go语言的filepath.Match规则。

Docker还支持**,匹配任意数量的目录(包括0)。如**/*.go排除.go结尾的,包括context root下所有目录。

如果排除了一堆文件后,想只包含其中几个文件,可以使用异常规则!

示例,排除.md结尾的文件,包含README.md

*.md
!README.md

README-secret.md不会被排除,因为!README*.md能匹配到README-secret.md,又把README-secret.md包含进来了。

.dockerignore文件甚至可以排除Dockerfile.dockerignore,然而并没有什么卵用,这些文件还是会被发送到Docker daemon,只是ADDCOPY命令不会把它们复制到镜像了。

FROM

FROM指令初始化一个新的buid stage,为后面的指令设置Parent Image。

FROM [--platform=<platform>] <image> [AS <name>]

FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]

FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

--platform,用来定义image的平台,如linux/amd64, linux/arm64, 或者windows/amd64,这样就能支持多平台镜像。

tag digest是可选的,都不填时,默认用最新的tag。如果找不到tag,builder就会报错。

AS name可以给image取个别名,在后续FROMCOPY --from=<name|index>指令中可以使用这个别名。

可以在一个Dockerfile文件中使用多个FROM。每个FROM都会把上个指令创建的状态清除。所以在每个新的FROM指令之前,记录commit输出的最后一个image ID。

ARG是唯一能在FROM之前的指令。

比如--platform,默认情况下,会使用build请求的默认平台。也可以使用全局build参数,通过automatic platform ARGs(依赖BuildKit)来强制把stage指定为本地build平台(--platform=$BUILDPLATFORM),然后用它来在stage中cross-compile目标平台。

FROMARG怎么结合使用呢?

FROM指令支持出现在第一个FROM之前的ARG声明的变量。

ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app FROM extras:${CODE_VERSION}
CMD /code/run-extras

FROM之前声明的ARG是在build stage之外的,所以它不能用在FROM后的任何指令中。如果要用,可以使用在build stage中的不带value的ARG指令,

ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version

RUN

  • RUN <command> (shell 格式,Linux /bin/sh -c Windowscmd /S /C)
  • RUN ["executable", "param1", "param2"] (exec 格式)

RUN指令会在当前镜像之上的新layer中执行命令,commit结果,commit后的镜像会在Dockerfile的下一个step中使用。

RUN指令的commits符合Docker理念,commit is cheap,containers可以从image历史中任何记录创建,就像source control。

可以使用不同的SHELL

shell格式

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

exec格式

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

shell格式会调用command shell,而exec格式不会,所以exec中$HOME是没用的,要用的话直接执行shell RUN [ "sh", "-c", "echo $HOME" ]

注意,exec格式被解析为JSON数组,所以只能用双引号。还需注意反斜杠,

错误

RUN ["c:\windows\system32\tasklist.exe"]

正确

RUN ["c:\\windows\\system32\\tasklist.exe"]

默认是会启动RUN的缓存的,比如RUN apt-get dist-upgrade -y会在下次build的时候复用。可以使用docker build --no-cache来禁用缓存。

使用ADDCOPY指令也可以禁用RUN缓存。

CMD

CMDRUN是不同的。RUN指令是在build过程中执行command和commit结果。CMD在build时不会执行任何command,而是为image定义command,在container(镜像创建的容器)启动的时候执行。

  • CMD ["executable","param1","param2"] (exec 格式,首选)
  • CMD ["param1","param2"] (ENTRYPOINT默认参数)
  • CMD command param1 param2 (shell 格式)

一个Dockerfile只能有一个CMD指令,如果有多个,只有最后一个生效。

shell格式会调用command shell,而exec格式不会,所以exec中$HOME是没用的,要用的话直接执行shell RUN [ "sh", "-c", "echo $HOME" ]

注意,exec格式被解析为JSON数组,所以只能用双引号。还需注意反斜杠。

如果想要container每次运行相同的可执行文件,需要结合 ENTRYPOINT使用。

如果docker run定义了参数,那么会覆盖CMD定义。

LABEL

LABEL <key>=<value> <key>=<value> <key>=<value> ...

LABEL用来给image添加metadata,是key-value键值对的形式。

示例,

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

一个image可以有多个label,一个label可以有多个键值对,以下是等价的,

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

label会随着image继承,从base image或parent image继承到当前image。

重复的label,会用最新的覆盖旧的。

可以使用命令查看image的labels,

docker image inspect --format='' myimage
{
"com.example.vendor": "ACME Incorporated",
"com.example.label-with-value": "foo",
"version": "1.0",
"description": "This text illustrates that label-values can span multiple lines.",
"multi.label1": "value1",
"multi.label2": "value2",
"other": "value3"
}

MAINTAINER

MAINTAINER 已经弃用了,直接使用LABLE

LABEL maintainer="SvenDowideit@home.org.au"

EXPOSE

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

EXPOSE定义了container监听的网络端口,支持TCP和UDP,默认TCP。

EXPOSE并不真正的发布端口,而只是一种预定义。

真正发布是在docker run的时候,使用-p-P来发布。

-p发布一个或多个端口,-P发布全部,并映射到高位端口。

示例,默认TCP,可以定义UDP,

EXPOSE 80/udp

也可以同时定义TCP和UDP,

EXPOSE 80/tcp
EXPOSE 80/udp

如果这里docker run使用了-P,将会暴露一次TCP端口和一次UDP端口,由于会映射到高位端口,它们的端口会不一样。

使用-p指定端口,

docker run -p 80:80/tcp -p 80:80/udp ...

也可以使用docker network来创建网络在container之间通信而不需要暴露任何端口。因为container可以使用任何端口通信。

ENV

ENV <key> <value>
ENV <key>=<value> ...

ENV用来设置环境变量。有2种形式,以下是等价的,

ENV myName="John Doe" myDog=Rex\ The\ Dog \
myCat=fluffy
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy

可以使用docker inspect来查看环境变量。也可以使用docker run --env <key>=<value>来修改环境变量。

ENV的作用域除了build,还包括container running。有时候会有副作用,比如ENV DEBIAN_FRONTEND noninteractive,所有操作都是非交互式的,无需向用户请求输入,直接运行命令。可能会使apt-get用户误认为是一个Debian-based image。正确的做法是为command添加单独的环境变量,如RUN apt-get install -y python3

ADD

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

ADD有2种形式,第2种是为了支持路径包含空格,所以加了双引号。

--chown只适用于Linux container,对Windows无效。

ADD的作用是从<src>复制新文件,目录或者远程文件URLs,然后添加到<desc>所在的image文件系统。

src如果是文件和目录,那么就是相对路径,相对于build的context。同时支持通配符,遵循Golang的filepath.Match规则。

示例,添加所有以"hom"开头的文件,

ADD hom* /mydir/

?匹配单个字符,

ADD hom?.txt /mydir/

<dest>是绝对路径,或者WORKDIR的相对路径。

示例,绝对路径,

ADD test.txt /absoluteDir/

相对路径,<WORKDIR>/relativeDir/

ADD test.txt relativeDir/

如果路径种包含特殊字符(如[]),那么需要进行转义,

示例,添加一个文件arr[0].txt

ADD arr[[]0].txt /mydir/

针对Linux,可以使用--chown定义username、groupname或者UID/GID,默认新文件和目录会被设置为UID为0,GID为0。

如果只设置username不设置groupname,或只设置UID不设置GID,GID会使用和UID相同的数值。

username和groupname会被container's root filesystem /etc/passwd and /etc/group 转换为UID/GID。如果container没有这2个文件,在设置了username/groupname后,就会报错。可以通过设置UID/GID来避免。

示例,

ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/

如果build使用STDIN (docker build - < somefile),就没有build context,就只能用ADDURL。也可以在使用STDIN时添加压缩包 (docker build - < archive.tar.gz),压缩包根目录的Dockerfile和其他压缩包会当做build context。

如果src是一个远程文件URL,就会需要600权限(Linux)。如果远程文件有HTTP Last-Modified header,header的timestamp会用来设置到dest文件的mtime。但是mtime不会反映文件是否修改和缓存是否应该更新。

如果URL文件需要授权,ADD是不支持的,需要使用RUN wget, RUN curl,或者container里面的其他工具。

ADD遵循以下规则:

  • <src> 必须在build的context 中;不能 ADD ../something /something添加context父目录的东西。因为 docker build 的第一步是把context,目录及其子目录发送到docker daemon。
  • 如果<src> 是URL,<dest> 没有以斜杠结尾,那么文件从直接从URL下载后,然后直接复制到 <dest>
  • 如果 <src> 是URL,<dest> 是以斜杠结尾的,那么会从URL解析出文件名,下载到<dest>/<filename>。比如, ADD http://example.com/foobar dest/ 会创建文件 dest/foobar。URL必须是明确的路径,以保证能找到合适的文件名(http://example.com 是无效的)。
  • 如果 <src> 是目录,那么整个目录都会被复制,包括文件系统的metadata。(目录本身不复制,只是内容)
  • 如果 <src>是本地压缩包(如gzip, bzip2 or xz),那么会被解压成目录。远程URL是不会解压的。解压相当于执行了 tar -x,如果dest路径下有文件冲突,会被重命名为“2”。(压缩包不是根据文件名判断的,而是根据内容,比如一个空文件命名为.tar.gz,是不会被解压复制的)
  • 如果 <src> 是任何其他文件,就会随同它的metadata一起复制。此时 <dest> 以斜杠 /结尾的话,就会被认为是一个目录,<src>的内容会被写到<dest>/base(<src>)
  • 如果<src>定义的是多个资源,不论是直接还是通配符匹配到的, <dest> 必须是一个目录,且以斜杠/结尾。
  • 如果 <dest> 不以斜杠结尾,那么就会被认为是一个普通文件,那么<src> 会被写到<dest>
  • 如果 <dest> 不存在,那么path中的所有未创建的目录都会自动创建。

如果src内容改变了,在第一次遇到ADD指令后,会禁用后续所有指令的缓存,包括RUN指令的缓存。

COPY

COPYADD区别在于ADD可以添加远程URLS,COPY不能。

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

COPY有2种形式,第2种是为了支持路径包含空格,所以加了双引号。

--chown只适用于Linux container,对Windows无效。

COPY的作用是从<src>复制新文件,目录,然后添加到<desc>所在的image文件系统。

src如果是文件和目录,那么就是相对路径,相对于build的context。同时支持通配符,遵循Golang的filepath.Match规则。

示例,添加所有以"hom"开头的文件,

COPY hom* /mydir/

?匹配单个字符,

COPY hom?.txt /mydir/

<dest>是绝对路径,或者WORKDIR的相对路径。

示例,绝对路径,

COPY test.txt /absoluteDir/

相对路径,<WORKDIR>/relativeDir/

COPY test.txt relativeDir/

如果路径种包含特殊字符(如[]),那么需要进行转义,

示例,添加一个文件arr[0].txt

COPY arr[[]0].txt /mydir/

针对Linux,可以使用--chown定义username、groupname或者UID/GID,默认新文件和目录会被设置为UID为0,GID为0。

如果只设置username不设置groupname,或只设置UID不设置GID,GID会使用和UID相同的数值。

username和groupname会被container's root filesystem /etc/passwd and /etc/group 转换为UID/GID。如果container没有这2个文件,在设置了username/groupname后,就会报错。可以通过设置UID/GID来避免。

示例,

COPY --chown=55:mygroup files* /somedir/
COPY --chown=bin files* /somedir/
COPY --chown=1 files* /somedir/
COPY --chown=10:11 files* /somedir/

如果build使用STDIN (docker build - < somefile),就没有build context,就不能用COPY

COPY支持--from=<name|index>,用来指定src为之前buid的image(通过FROM .. AS <name>创建的)来替换build context。既可以是name也可以是index数字(所有使用FROM指令建立的build stages)。如果通过name找不到build stage,就会去找同名的image。

COPY遵循以下规则:

  • <src> 必须在build的context 中;不能 COPY ../something /something添加context父目录的东西。因为 docker build 的第一步是把context,目录及其子目录发送到docker daemon。
  • 如果 <src> 是目录,那么整个目录都会被复制,包括文件系统的metadata。(目录本身不复制,只是内容)
  • 如果 <src> 是任何其他文件,就会随同它的metadata一起复制。此时 <dest> 以斜杠 /结尾的话,就会被认为是一个目录,<src>的内容会被写到<dest>/base(<src>)
  • 如果<src>定义的是多个资源,不论是直接还是通配符匹配到的, <dest> 必须是一个目录,且以斜杠/结尾。
  • 如果 <dest> 不以斜杠结尾,那么就会被认为是一个普通文件,那么<src> 会被写到<dest>
  • 如果 <dest> 不存在,那么path中的所有未创建的目录都会自动创建。

如果src内容改变了,在第一次遇到COPY指令后,会禁用后续所有指令的缓存,包括RUN指令的缓存。

ENTRYPOINT

exec 格式

ENTRYPOINT ["executable", "param1", "param2"]

shell 格式

ENTRYPOINT command param1 param2

ENTRYPOINT用来配置container作为可执行文件来运行。

示例,使用默认内容启动nginx,监听80端口,

$ docker run -i -t --rm -p 80:80 nginx

docker run <image>的命令行参数,会被添加到exec格式中的所有元素之后,并覆盖CMD指令定义的元素。这样就可以把参数传递给entry point,也就是docker run <image> -d会把-d传递给entry point。可以使用docker run --entrypoint来覆盖ENTRYPOINT指令(但是只能把binary设置为exec,不能用sh -c)。

shell格式会禁用掉CMD或者run命令行参数,但是有个缺点就是,ENTRYPOINT就不是作为/bin/sh -c的子命令来启动的了,也就是不能传递signals。也就意味着可执行文件,不是container的PID 1,也不会接收Unix signals(一种软件中断)。这样可执行文件就不会接收来自docker stop <container>SIGTERM

只有Dockerfile的最后一个ENTRYPOINT才会生效。

ENTRYPOINT Exec示例

FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

当运行container,top是唯一进程,

$ docker run -it --rm --name test  top -H

top - 08:25:00 up  7:27,  0 users,  load average: 0.00, 0.01, 0.05
Threads: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.1 us, 0.1 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem: 2056668 total, 1616832 used, 439836 free, 99352 buffers
KiB Swap: 1441840 total, 0 used, 1441840 free. 1324440 cached Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 19744 2336 2080 R 0.0 0.1 0:00.04 top

为了验证更多结果,使用docker exec

$ docker exec -it test ps aux

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root 1 2.6 0.1 19752 2352 ? Ss+ 08:24 0:00 top -b -H
root 7 0.0 0.1 15572 2164 ? R+ 08:25 0:00 ps aux

top -b -H,其中top -bENTRYPOINT设置的,-H是docker命令行参数,添加到了ENTRYPOINT后面,覆盖了CMD-c。

然后可以优雅地使用docker stop test请求top shut down。

示例,使用ENTRYPOINT在前台运行Apache(也就是PID 1),

FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

如果想编写单个可执行文件的启动脚本,可以使用execgosu命令,来确保可执行文件能够接收到Unix signals。

#!/usr/bin/env bash
set -e if [ "$1" = 'postgres' ]; then
chown -R postgres "$PGDATA" if [ -z "$(ls -A "$PGDATA")" ]; then
gosu postgres initdb
fi exec gosu postgres "$@"
fi exec "$@"

最后,如果在shutdown的时候需要做一些额外的清理(或者和其他containers交互),或者是多个协调而不是单个可执行文件,就可能需要确保ENTRYPOINT脚本能够接收Unix signals,传递,然后做更多工作,

#!/bin/sh
# Note: I've written this using sh so it works in the busybox container too # USE the trap if you need to also do manual cleanup after the service is stopped,
# or need to start multiple services in the one container
trap "echo TRAPed signal" HUP INT QUIT TERM # start service in background here
/usr/sbin/apachectl start echo "[hit enter key to exit] or run 'docker stop <container>'"
read # stop service and clean up here
echo "stopping apache"
/usr/sbin/apachectl stop echo "exited $0"

如果使用docker run -it --rm -p 80:80 --name test apache来运行这个image,那么就可以使用docker execdocker top来验证container处理,然后使用脚本停止Apache,

$ docker exec -it test ps aux

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root 1 0.1 0.0 4448 692 ? Ss+ 00:42 0:00 /bin/sh /run.sh 123 cmd cmd2
root 19 0.0 0.2 71304 4440 ? Ss 00:42 0:00 /usr/sbin/apache2 -k start
www-data 20 0.2 0.2 360468 6004 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
www-data 21 0.2 0.2 360468 6000 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
root 81 0.0 0.1 15572 2140 ? R+ 00:44 0:00 ps aux $ docker top test PID USER COMMAND
10035 root {run.sh} /bin/sh /run.sh 123 cmd cmd2
10054 root /usr/sbin/apache2 -k start
10055 33 /usr/sbin/apache2 -k start
10056 33 /usr/sbin/apache2 -k start $ /usr/bin/time docker stop test test
real 0m 0.27s
user 0m 0.03s
sys 0m 0.03s

shell格式会调用command shell,而exec格式不会,所以exec中$HOME是没用的,要用的话直接执行shell RUN [ "sh", "-c", "echo $HOME" ]

注意,exec格式被解析为JSON数组,所以只能用双引号。还需注意反斜杠。

ENTRYPOINT Shell示例

ENTRYPOINT定义一个简单的string,然后它就会在/bin/sh -c中执行。shell格式使用shell processing来替代shell environment variables,然后会忽略任何CMDdocker run命令行参数。为了确保docker stop能直接signal任何运行的ENTRYPOINT可执行文件,记住使用exec开始,

FROM ubuntu
ENTRYPOINT exec top -b

运行这个image时,你会看到单个PID 1进程,

$ docker run -it --rm --name test top

Mem: 1704520K used, 352148K free, 0K shrd, 0K buff, 140368121167873K cached
CPU: 5% usr 0% sys 0% nic 94% idle 0% io 0% irq 0% sirq
Load average: 0.08 0.03 0.05 2/98 6
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
1 0 root R 3164 0% 0% top -b

执行docker stop,也会干净的退出,

$ /usr/bin/time docker stop test

test
real 0m 0.20s
user 0m 0.02s
sys 0m 0.04s

如果忘了在ENTRYPOINT前添加exec

FROM ubuntu
ENTRYPOINT top -b
CMD --ignored-param1

运行(为下一步设置一个name),

$ docker run -it --name test top --ignored-param2

Mem: 1704184K used, 352484K free, 0K shrd, 0K buff, 140621524238337K cached
CPU: 9% usr 2% sys 0% nic 88% idle 0% io 0% irq 0% sirq
Load average: 0.01 0.02 0.05 2/101 7
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
1 0 root S 3168 0% 0% /bin/sh -c top -b cmd cmd2
7 1 root R 3164 0% 0% top -b

你就会看到ENTRYPOINT定义的top不是PID 1

如果执行docker stop test,container就不会干净地退出。stop命令会在超时后被强制发送一个SIGKILL

$ docker exec -it test ps aux

PID   USER     COMMAND
1 root /bin/sh -c top -b cmd cmd2
7 root top -b
8 root ps aux $ /usr/bin/time docker stop test test
real 0m 10.19s
user 0m 0.04s
sys 0m 0.03s

real 10.19s超时。

CMD和ENTRYPOINT如何结合使用

CMDENTRYPOINT指令都定义了运行container时,哪些命令会执行。他们的结合有一些规则,

  1. Dockerfile应该定义至少一个CMDENTRYPOINT
  2. 如果使用container作为可执行文件,应该定义ENTRYPOINT
  3. 如果需要给ENTRYPOINT 定义默认参数,或者在container中执行ad-hoc(临时)命令,应该使用CMD
  4. 以可选参数运行container时会覆盖CMD

下面这个表格展示了CMDENTRYPOINT指令的不同组合

No ENTRYPOINT ENTRYPOINT exec_entry p1_entry ENTRYPOINT [“exec_entry”, “p1_entry”]
No CMD error, not allowed /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

注意,如果CMD是从base image定义的,那么设置ENTRYPOINT会重置CMD为空值。此时如果要使用CMD,必须在当前image重新定义。

VOLUME

VOLUME ["/data"]

VOLUME指令用来创建挂载点,把container挂载到native host(宿主机)或其他container。

value可以是JSON array,如VOLUME ["/var/log/"],也可以是string,如VOLUME /var/logVOLUME /var/log /var/db

docker run命令会用base image中定义的location中存在的任何数据,来初始化新创建的volumn。

示例,

FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol

docker run会在/myvol创建一个挂载点,然后把greeting复制到新创建的volumn。

遵循规则,

  • **基于Windows的containers **: volumn的目标路径必须是以下之一:

    • 不存在的或者空的目录
    • C:以外的驱动
  • 在Dockerfile里面修改volumn: 在volumn已经被声明之后的任何build steps尝试修改volumn数据,都会被忽略。
  • JSON formatting: 要用双引号,不要用单引号.
  • 在container run-time才会声明主机目录(挂载点): 挂载点是依赖主机的。因为主机目录不能保证对所有主机都是有用的,为了保证image的可移植性,不能在Dockerfile中挂载主机目录,而是必须在创建或运行container的时候。VOLUME指令也不支持host-dir这样的参数。

USER

USER <user>[:<group>]

USER <UID>[:<GID>]

USER指令用于RUN, CMDENTRYPOINT指令执行时指定user name / group。USER指令可以设置user name(或UID),可选用user group(或GID)。

如果定义了user group,那么这个user就只有这个group的membership,任何其他配置的group memberships都会被忽略。

如果user没有primary group,那么image(或者下一条指令)就会以root group运行。

在Windows,如果不是内建账号,必须先创建。可以在Dockerfile中调用net user命令,

FROM microsoft/windowsservercore
# Create Windows user in the container
RUN net user /add patrick
# Set it for subsequent commands
USER patrick

WORKDIR

WORKDIR /path/to/workdir

WORKDIRRUN, CMD, ENTRYPOINT, COPY and ADD指令设置工作目录。

如果WORKDIR不存在,即使后面的Dockerfile不会用到,它仍然会被创建。

WORKDIR指令可以在Dockerfile中定义多次。如果是相对路径,那么就是相对于上一条WORKDIR指令的路径。

示例,

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

pwd的结果是/a/b/c

WORKDIR可以引用ENV定义的环境变量,示例,

ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

pwd的结果是/path/$DIRNAME

ARG

ARG <name>[=<default value>]

ARG指令定义变量,用户可以在使用docker build命令带参数--build-arg <varname>=<value>,在build-time传递这个变量给builder。如果用户指定了一个build参数而没有在Dockerfile中定义,build会报warning,

[Warning] One or more build-args [foo] were not consumed.

一个Dockerfile可以包含一个或多个ARG指令。

示例,

FROM busybox
ARG user1
ARG buildno
# ...

警告!不建议使用build-time变量来传递私密数据,如github keys,用户认证信息等。因为image的任何用户都可以使用docker history查看build-time变量。

默认值

ARG指令可以设置默认值(可选),

FROM busybox
ARG user1=someuser
ARG buildno=1
# ...

如果ARG指令有默认值,在build-time没有值传递,那么builder会用这个默认值。

范围

ARG指令是在它被定义那一行生效的,而不是命令行被使用的时候,或者其他地方。

示例,

FROM busybox
USER ${user:-some_user}
ARG user
USER $user
# ...

用户build这个文件,调用,

$ docker build --build-arg user=what_user .

第2行的USER结果为some_user因为user变量是在第3行定义的。

第4行的USER结果为what_user,因为user变量已经被定义了,在命令行传递了what_user值。

ARG指令定义之前,任何变量使用结果都是空string。

ARG定义的build stage结束时,ARG指令就超出范围了。为了在多个stages使用同一个arg,每个stage都必须包括ARG指令,

FROM busybox
ARG SETTINGS
RUN ./run/setup $SETTINGS FROM busybox
ARG SETTINGS
RUN ./run/other $SETTINGS

使用ARG变量

可以使用ARGENV指令来为RUN指令定义变量。ENV定义的环境变量始终都会覆盖ARG定义的同名变量。

示例,

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER v1.0.0
RUN echo $CONT_IMG_VER

假设使用这条命令build image,

$ docker build --build-arg CONT_IMG_VER=v2.0.1 .

RUN会使用v1.0.0而不是ARG传递的v2.0.1。这个行为有点类似于shell脚本,一个局部变量会覆盖通过参数传递的变量,或者从环境定义继承的变量。

还是上面的例子,定义不同的ENV会把ARGENV结合的更好用,

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER

不像ARGENV的值会在build image中持久化。如果不用--build-arg build,

$ docker build .

用这个Dockerfile,CONT_IMG_VER仍然会持久化在这个image,它的值是v1.0.0,因为在第3行用ENV定义了默认值。

在这个示例中,通过ENV指令,可以把命令行参数传递进来,然后持久化到最终的image,实现了变量扩展。变量扩展只支持Dockerfile指令的一部分指令。

  • ADD
  • COPY
  • ENV
  • EXPOSE
  • FROM
  • LABEL
  • STOPSIGNAL
  • USER
  • VOLUME
  • WORKDIR
  • ONBUILD (结合以上指令使用)

预定义ARGs

Docker有一些预定义的ARG变量,你可以不使用ARG指令,直接用这些变量。

  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • FTP_PROXY
  • ftp_proxy
  • NO_PROXY
  • no_proxy

直接在命令行使用,

--build-arg <varname>=<value>

默认这些预定义的变量是不会输出到docker history中的。这样可以降低在HTTP_PROXY变量中意外泄露敏感认证信息的风险。

示例,使用--build-arg HTTP_PROXY=http://user:pass@proxy.lon.example.com来build Dockerfile,

FROM ubuntu
RUN echo "Hello World"

HTTP_PROXY变量不会输出到docker history,也不会被缓存。如果代理服务器变成了http://user:pass@proxy.sfo.example.com,后续的build不会导致cache miss。

可以使用ARG来覆盖这个默认行为,

FROM ubuntu
ARG HTTP_PROXY
RUN echo "Hello World"

当build这个Dockerfile的时候,HTTP_PROXY会存到docker history中,如果它的值改变了,会把build缓存禁用掉。

对缓存的影响

ARG变量并不会像ENV持久化到image,但是会以类似的方式,影响到build缓存。如果Dockerfile定义了一个ARG变量,这个变量和前一个build不一样,那么在第一次用这个变量的时候会发生"cache miss"(不是定义的时候)。尤其是,所有ARG后面的RUN指令一般都会使用ARG变量,这样就会导致cache miss。但是所有预定义ARGs是没有影响cache的,除非是在Dockerfile中有一个同名的ARG指令。

示例,2个Dockerfile

FROM ubuntu
ARG CONT_IMG_VER
RUN echo $CONT_IMG_VER
FROM ubuntu
ARG CONT_IMG_VER
RUN echo hello

如果在命令行指定--build-arg CONT_IMG_VER=<value>,以上2个示例在第2行都不会cache miss,第3行会cache miss。ARG CONT_IMG_VER会导致RUN那一行被认为是执行了CONT_IMG_VER=<value> echo hello,所以如果<value>改变了,就cache miss了。

另外一个示例,

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER $CONT_IMG_VER
RUN echo $CONT_IMG_VER

第3行会发生cache miss。因为ENV引用的ARG变量通过命令行改变了。另外,在这个示例中,ENV会导致image包含这个value(ENV会持久化到image中)。

如果ENVARG指令重复,

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER hello
RUN echo $CONT_IMG_VER

第3行就不会发生cache miss,因为CONT_IMG_VER的值是常量(hello)。因此第4行RUN指令用到的环境变量和值在build之间不会改变。

ONBUILD

ONBUILD <INSTRUCTION>

ONBUILD指令会在image中添加一个trigger,这个trigger会在image作为base的时候触发。trigger会在下游的 build context中执行,就像在下游的Dockerfile 中,在 FROM指令之后,它就已经被立即嵌入了。

任何build指令都可以注册为trigger。

如果你build一个image,这个image会作为base来build其他images,这就很有用。比如,一个应用build环境或者一个deamon自定义配置。

示例,如果一个image是可复用的Python应用builder(用来build新的应用image),那么它需要把应用源码添加到一个特定目录,然后调用build脚本。此时ADDRUN指令是无法访问应用源码的,每个应用build的源码也可能不一样。你可以简单地,给应用开发者提供Dockerfile样本文件来复制粘贴到他们的应用中,但这是低效、易出错和困难去做更新的,因为这个和“应用定义”代码混淆了。

可以使用ONBUILD指令来提前注册指令,在下个build stage再运行。

过程如下,

  1. 当碰到ONBUILD 指令,builder就会添加trigger到正在build的image的metadata。这条指令不会影响当前build。
  2. 在build的最后,所有的triggers都会被存储到image的manifest,在key OnBuild下面。可以用 docker inspect 命令查看。
  3. 然后image可能会被用来作为新build的base,使用 FROM 指令。 FROM 指令在处理时,下游builder会查找 ONBUILD triggers,然后按它们注册的顺序执行。如果有trigger失败了,FROM指令就会中断,build失败。如果triggers都成功了,那么FROM会完成,build成功。
  4. Triggers会在执行后,从最后一个image中清除。也就是说,它们是不会随着“父子”build继承的。

比如你可能会添加这样的内容,

ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src

注意,1.链式ONBUILD ONBUILD是不允许的。2.ONBUILD可能不会trigger FROMMAINTAINER指令。

STOPSIGNAL

STOPSIGNAL signal

STOPSIGNAL指令设置system call signal,发送到container退出。signal可以是有效的unsigned number(匹配kernel’s syscall table里的position,比如9),也可以是SIGNAME(比如SIGKILL)。

HEALTHCHECK

2种格式,

  • HEALTHCHECK [OPTIONS] CMD command (通过运行container里面的命令来检查container)
  • HEALTHCHECK NONE (禁用健康检查,从base image继承)

HEALTHCHECK 指令用来告诉Docker怎样测试container是否还在工作。比如虽然server一直在运行,但是实际上已经死循环了,无法处理新连接了。

当container定义了健康检查,就会把健康状态添加到status中。status初始化是starting。无论健康检查什么时候通过,它都会变为healthy(无论之前是什么状态)。在一定数量的连续失败后,它会变为unhealthy

第一种格式的OPTION可以是,

  • --interval=DURATION (default: 30s)
  • --timeout=DURATION (default: 30s)
  • --start-period=DURATION (default: 0s)
  • --retries=N (default: 3)

在container开始后的interval seconds ,会运行健康检查。每个健康检查完成后,等待interval seconds再次运行。

如果健康检查运行的时候超过了timeout seconds,就认为失败。

失败的次数如果达到了retries的值,就认为unhealthy

start period指定了container需要启动的时间。在这期间探针失败(Probe failure)不会记作重试次数。但是,如果在这期间健康检查通过了,那么container就认为已经启动了,这之后的失败(all consecutive failures)就会记作重试次数。

一个Dockerfile只能有一个HEALTHCHECK 指令。如果有多个,那么只有最后一个HEALTHCHECK 生效。

第1种格式的command既可以是shell命令(如,HEALTHCHECK CMD /bin/check-running),也可以是exec数组。

command的退出状态反应了container的健康状态,

  • 0: success - the container is healthy and ready for use
  • 1: unhealthy - the container is not working correctly
  • 2: reserved - do not use this exit code

示例,每5分钟检查1次,以确保web服务器能在3秒内为网站首页提供服务,

HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1

为了帮助debug失败探针(failing probes),任何写到stdout或stderr输出文本(UTF-8编码)都会被存储到健康状态,并且可以使用docker inspect查询。而且输出应该简短(目前只有最开始的4096 bytes会被存储)。

当container的健康状态改变了,会用新的状态生成一个health_status事件。

SHELL

SHELL ["executable", "parameters"]

SHELL指令允许重写shell格式命令的默认shell。Linux的默认shell是["/bin/sh", "-c"],Windows的默认shell是["cmd", "/S", "/C"]SHELL指令必须在Dockfile中写成JSON格式。

SHELL指令在Windows特别有用,因为Windows有2个常用的不同的原生shell,cmdpowershell,也有可选用的shell,包括sh

SHELL指令可以出现多次。每个SHELL指令会覆盖所有之前的SHELL指令,影响随后的指令。

示例,

FROM microsoft/windowsservercore

# Executed as cmd /S /C echo default
RUN echo default # Executed as cmd /S /C powershell -command Write-Host default
RUN powershell -command Write-Host default # Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello # Executed as cmd /S /C echo hello
SHELL ["cmd", "/S", "/C"]
RUN echo hello

shell格式的RUN CMDENTRYPOINT出现在Dcokerfile中时,SHELL指令能影响这些指令。

示例,Windows上常见的模式,可以通过使用SHELL指令进行简化,

RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"

docker调用的命令,

cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"

这个有点低效,有2个原因。首先,有一个不必要的cmd.exe命令行处理器(aka shell)被调用了。其次,shell格式的RUN指令需要额外的前缀命令powershell -command

为了更高效,有2种机制。其一是使用JSON格式,

RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]

JSON格式是清晰的,不会使用不必要的cmd.exe。但是需要双引号和转义符,显得有点冗余。

。其二是用SHELL指令和shell格式,这样可以给Windows用户更自然的语法,特别是和escape parser directive结合使用的时候,

# escape=`

FROM microsoft/nanoserver
SHELL ["powershell","-command"]
RUN New-Item -ItemType Directory C:\Example
ADD Execute-MyCmdlet.ps1 c:\example\
RUN c:\example\Execute-MyCmdlet -sample 'hello world'

结果是,

PS E:\docker\build\shell> docker build -t shell .
Sending build context to Docker daemon 4.096 kB
Step 1/5 : FROM microsoft/nanoserver
---> 22738ff49c6d
Step 2/5 : SHELL powershell -command
---> Running in 6fcdb6855ae2
---> 6331462d4300
Removing intermediate container 6fcdb6855ae2
Step 3/5 : RUN New-Item -ItemType Directory C:\Example
---> Running in d0eef8386e97 Directory: C:\ Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 10/28/2016 11:26 AM Example ---> 3f2fbf1395d9
Removing intermediate container d0eef8386e97
Step 4/5 : ADD Execute-MyCmdlet.ps1 c:\example\
---> a955b2621c31
Removing intermediate container b825593d39fc
Step 5/5 : RUN c:\example\Execute-MyCmdlet 'hello world'
---> Running in be6d8e63fe75
hello world
---> 8e559e9bf424
Removing intermediate container be6d8e63fe75
Successfully built 8e559e9bf424
PS E:\docker\build\shell>

SHELL指令也能被用来修改shell操作方式。比如在Windows用SHELL cmd /S /C /V:ON|OFF,可以修改延迟环境变量扩展语义。

SHELL指令也可以用在Linux上,可选的shell有zsh, csh, tcsh等。

Dockerfile示例

# Nginx
#
# VERSION 0.0.1 FROM ubuntu
LABEL Description="This image is used to start the foobar executable" Vendor="ACME Products" Version="1.0"
RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server
# Firefox over VNC
#
# VERSION 0.3 FROM ubuntu # Install vnc, xvfb in order to create a 'fake' display and firefox
RUN apt-get update && apt-get install -y x11vnc xvfb firefox
RUN mkdir ~/.vnc
# Setup a password
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way, but it does the trick)
RUN bash -c 'echo "firefox" >> /.bashrc' EXPOSE 5900
CMD ["x11vnc", "-forever", "-usepw", "-create"]
# Multiple images example
#
# VERSION 0.1 FROM ubuntu
RUN echo foo > bar
# Will output something like ===> 907ad6c2736f FROM ubuntu
RUN echo moo > oink
# Will output something like ===> 695d7793cbe4 # You'll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with
# /oink.

以下内容可查看参考资料进一步阅读。

  • BuildKit(第三方工具)
  • Parser directives的命令syntax(依赖BuildKit)
  • RUN已知bug(Issue 783
  • External implementation features(依赖BuildKit)
  • Automatic platform ARGs in the global scope(依赖BuildKit)

参考资料

https://docs.docker.com/engine/reference/builder/

下一篇《Dockerfile最佳实践》,欢迎持续关注哦。

版权申明:本文为博主原创文章,转载请保留原文链接及作者。


专注测试,坚持原创,只做精品。

Dockerfile文件万字全面解析的更多相关文章

  1. Dockerfile 指令汇总及解析

        原文地址:http://www.maoyupeng.com/dockerfile-command-introduction.html 什么是Dockerfile Dockerfile是由一系列 ...

  2. Dockerfile文件记录(用于后端项目部署)

    Dockerfile文件记录(用于后端项目部署) 本教程依据个人理解并经过实际验证为正确,特此记录下来,权当笔记. 注:基于linux操作系统(敏感信息都进行了处理) 此文结合另一篇博客共同构成后端服 ...

  3. Docker学习(六)——Dockerfile文件详解

    Docker学习(六)--Dockerfile文件详解 一.环境介绍 1.Dockerfile中所用的所有文件一定要和Dockerfile文件在同一级父目录下,可以为Dockerfile父目录的子目录 ...

  4. 『现学现忘』Docker基础 — 34、DockerFile文件详解

    目录 1.DockerFile文件说明 2.Dockerfile构建过程解析 (1)Docker容器构建三步骤 (2)Dockerfile文件的基本结构 (3)Dockerfile注意事项 (4)Do ...

  5. PNG文件结构分析 ---Png解析

    PNG文件结构分析 ---Png解析   为了实现更高级的应用,我们必须充分挖掘PNG的潜力. PNG的文件结构 根据PNG文件的定义来说,其文件头位置总是由位固定的字节来描述的:   十进制数 13 ...

  6. NodeJs之word文件生成与解析

    NodeJs之word文件生成与解析 一,介绍与需求 1.1,介绍 1,officegen模块可以为Microsoft Office 2007及更高版本生成Office Open XML文件.此模块不 ...

  7. 【Docker】(6)---Dockerfile文件

    springboot项目打包镜像中的Dockerfile文件 在讲Dockerfile文件之前应该先讲下maven的一个插件叫:dockerfile-maven-plugin.他们是组合一起用的 一. ...

  8. docker18.09.5 Dockerfile文件编写

    Dockerfile命令详解(超全版本)  https://www.cnblogs.com/dazhoushuoceshi/p/7066041.html 案例1 dockerfile文件内容: FRO ...

  9. C#windows桌面应用小程序制作——大文件数据分段解析存储

    现在的任务就是做一个大文件解析的桌面应用小程序,具体需求就是:将一个很大的文件里的数据按一定标志拆分然后分别保存到某个文件夹下面. 解析的文件内容为以下内容: windows 应用小程序界面 具体代码 ...

随机推荐

  1. vue同时安装element ui跟 vant

    记一个卡了我比较久的问题,之前弄的心态爆炸各种问题. 现在来记录一下,首先我vant是已经安装成功了的. 然后引入element ui npm i element-ui -S 接着按需引入,安装插件 ...

  2. Kafka和SpringBoot

    事先必备: kafka已安装完成 1.目录结构 2.父pom <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns ...

  3. PHP strpbrk() 函数

    实例 在字符串中搜索字符 "oe",并返回字符串中从指定字符第一次出现的位置开始的剩余部分: <?php高佣联盟 www.cgewang.comecho strpbrk(&q ...

  4. CF R639 div 2 E Quantifier Question 数学 dfs 图论

    LINK:Quantifier Question 题面过长 引起不适 读题花了好长时间 对于 和 存在符合不是很熟练 导致很懵逼的做完了. 好在还算很好想.不过wa到了一个坑点上面 自闭一大晌 还以为 ...

  5. 省选模拟赛day4

    怎么说?发现自己越来越菜了 到了不写题解写不出来题目的地步了.. 这次题目我都有认真思考 尽管思考的时候状态不太好 但是 我想 再多给我时间也思考不出来什么吧 所以写一份题解. T1 n个点的有根树 ...

  6. .Net Core 实体生成器

    实体生成器是什么? 实体生成器的功能就是自动将数据库中的表以及字段 转化成我们 高级编程语言中的实体类. 我们为什么要用实体生成器 在.net core开发环境下,我们可以使用efcore这个orm来 ...

  7. Web优化躬行记(3)——图像和网络

    一.图像 1)响应式图像 浏览器根据屏幕大小.设备像素比.横竖屏自动加载合适的图像. 响应式的功能可以通过srcset和sizes两个新属性实现. 前者可指定选择的图像以及其大小,后者会定义一组媒体条 ...

  8. CAS(乐观锁)以及ABA问题

    https://blog.csdn.net/wwd0501/article/details/88663621独占锁是一种悲观锁,synchronized就是一种独占锁:它假设最坏的情况,并且只有在确保 ...

  9. Python实现图片滑动式验证识别

    1 abstract 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识.那么针对这三类 ...

  10. PHP入门之数组

    前言 之前几篇文章分别介绍了PHP的运算符,流程控制,函数.有兴趣的可以去看看. PHP入门之类型与运算符 PHP入门之流程控制 PHP入门之函数 接下来简单介绍一下数组. 数组初探 为什么要引进数组 ...