欢迎你的到来,神圣知识宝库将为你的成长保驾护航~~

深入dockerfile


深入Dockerfile

Dockerfile由一行行命令语句组成,并且支持以#开头的注释行。 (文件名无后缀)

基础的小linux系统。如:jdk

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

  • 基础镜像信息

  • 维护者信息

  • 镜像操作指令

  • 启动时执行指令

    指令 说明
    FROM 指定基础镜像
    MAINTAINER 指定维护者信息,已经过时,可以使用LABEL maintainer=xxx 来代替
    RUN 运行命令
    CMD 指定启动容器时默认的命令
    ENTRYPOINT 指定镜像的默认入口.运行命令
    EXPOSE 声明镜像内服务监听的端口
    ENV 指定环境变量,可以在docker run的时候使用-e改变 ;会被固化到image的 config里面
    ADD 复制指定的src路径下的内容到容器中的dest路径下,src可以为url会自动下载, 可以为tar文件,会自动解压
    COPY 复制本地主机的src路径下的内容到镜像中的dest路径下,但不会自动解压等
    LABEL 指定生成镜像的元数据标签信息
    VOLUME 创建数据卷挂载点
    USER 指定运行容器时的用户名或UID
    WORKDIR 配置工作目录,为后续的RUN、CMD、ENTRYPOINT指令配置工作目录
    ARG 指定镜像内使用的参数(如版本号信息等),可以在build的时候,使用–build-args改变
    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=hzhero xx=aa
    ENV msg='hello hzhero'
    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 <command>的方式,
    2. exec ["/bin/sh","-c",command] 的方式== shell方式
    也就是exec 默认方式不会进行变量替换

4、CMD和ENTRYPOINT(都是只在运行时执行)

0、都可以作为容器启动入口

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=hzhero
CMD ["1111"]
CMD ["2222"]
ENTRYPOINT ["echo"]
#构建出如上镜像后测试
docker run xxxx:效果 echo 2222

1、只能有一个CMD

  • Dockerfile中只能有一条CMD指令。 如果您列出多个CMD,则只有最后一个CMD才会生效。

  • CMD的主要目的是为执行中的容器提供默认值。 这些默认值可以包含可执行文件,也可以省略可 执行文件,在这种情况下,您还必须指定ENTRYPOINT指令。

2、CMD为ENTRYPOINT提供默认参数

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

3、组合最终效果

无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共同作用

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

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

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

5、ARG和ENV(ENV会覆盖ARG)

1、ARG(构建参数,只能在构建时RUN打印,运行时CMD ENTRYPOINT打印不出来)

  • 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的值,会改变哪些值,如何修改这些值?
    docker build --no-cache -t demo:test  -f dockerfile1 . 
    [root@localhost hzh]# docker run --env arg=222 bda9843ece19
    1111111

3、综合测试示例

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

[root@localhost hzh]# docker build --no-cache -t demo:test  -f dockerfile3 .
Sending build context to Docker daemon   5.12kB
Step 1/8 : FROM alpine
 ---> c059bfaa849c
Step 2/8 : ARG arg1=22222
 ---> Running in f268afb15919
Removing intermediate container f268afb15919
 ---> b9cb5acbcf8a
Step 3/8 : ENV arg2=1111111
 ---> Running in 02b53262b28a
Removing intermediate container 02b53262b28a
 ---> a6374e1227eb
Step 4/8 : ENV arg3=3333333
 ---> Running in 4429a64647d2
Removing intermediate container 4429a64647d2
 ---> b2867fe9fcd8
Step 5/8 : ENV runcmd=$arg1
 ---> Running in 6258b73903b5
Removing intermediate container 6258b73903b5
 ---> 08a713f21542
Step 6/8 : ENV runcmd2=$arg3
 ---> Running in d8b4496877bc
Removing intermediate container d8b4496877bc
 ---> 10681685a6c6
Step 7/8 : RUN echo $arg1 $arg2 $runcmd $runcmd2
 ---> Running in d9f073299461
22222 1111111 22222 3333333
Removing intermediate container d9f073299461
 ---> 479679fbcd8f
Step 8/8 : CMD echo $arg1 $arg2 $runcmd $runcmd2
 ---> Running in a789f06f3d71
Removing intermediate container a789f06f3d71
 ---> 0d9f14f33ef1
Successfully built 0d9f14f33ef1
Successfully tagged demo:test
[root@localhost hzh]# docker run -e arg1="444",arg3="555" 0d9f14f33ef1
444,arg3=555 1111111 22222 3333333
[root@localhost hzh]# docker run -e arg1="444" -e arg3="555" 0d9f14f33ef1
444 1111111 22222 3333333


#arg1 被覆盖为ENV 所以被打印了
#arg3先被持久化 所以$runcmd2打印为3333333

6、ADD和COPY

1、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/

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声明之前修改内容 ;

FROM alpine

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

#VOLUME挂载出去的东西,容器改变也不会最终commit的时候生效
#只是为了方便容器启动后,宿主机和容器直接方便文件传输或者同步修改
VOLUME ["/hello", "/app"]
#VOLUME 指定的挂载目录

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

CMD ping baidu.com


[root@localhost hzh]# docker build --no-cache -t demo:test  -f dockerfile4 .
Sending build context to Docker daemon  6.656kB
Step 1/8 : FROM alpine
 ---> c059bfaa849c
Step 2/8 : RUN mkdir /hello &&mkdir /app
 ---> Running in caca1ee42d10
Removing intermediate container caca1ee42d10
 ---> 144f38a938d5
Step 3/8 : RUN echo 1111 > /hello/a.txt
 ---> Running in 1296256552cd
Removing intermediate container 1296256552cd
 ---> 07cf4f85ada9
Step 4/8 : RUN echo 2222 > /app/b.txt
 ---> Running in c22bb65909f8
Removing intermediate container c22bb65909f8
 ---> fbee52567277
Step 5/8 : VOLUME ["/hello", "/app"]
 ---> Running in c43f6ed5b9fa
Removing intermediate container c43f6ed5b9fa
 ---> d1a87b02d53c
Step 6/8 : RUN echo 6666 >>  /hello/a.txt
 ---> Running in 8ef8a7702887
Removing intermediate container 8ef8a7702887
 ---> 41b731518e9b
Step 7/8 : RUN echo 8888 >> /app/b.txt
 ---> Running in e2778101beb6
Removing intermediate container e2778101beb6
 ---> 006843ded13f
Step 8/8 : CMD ping baidu.com
 ---> Running in 0ab295c77c66
Removing intermediate container 0ab295c77c66
 ---> c84867879973
Successfully built c84867879973
Successfully tagged demo:test

[root@localhost hzh]# docker run -d -P  c84867879973
17b209864395fd7419a0bb22370cc4839d85d115db9a1f1c7a5947236dca548b
[root@localhost hzh]# docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS     NAMES
17b209864395   c84867879973   "/bin/sh -c 'ping ba…"   3 seconds ago   Up 2 seconds             intelligent_lichterman

[root@localhost hzh]# docker inspect 17b209864395
[
    {
        "Id": "17b209864395fd7419a0bb22370cc4839d85d115db9a1f1c7a5947236dca548b",
        "Created": "2022-01-12T10:11:28.557803298Z",
        "Path": "/bin/sh",
        "Args": [
            "-c",
            "ping baidu.com"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 24882,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2022-01-12T10:11:28.959047886Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:c84867879973ca0bb31470be36cd43b9c7191c9bfb22fcbad923eb7cfa9ff16b",
        "ResolvConfPath": "/var/lib/docker/containers/17b209864395fd7419a0bb22370cc4839d85d115db9a1f1c7a5947236dca548b/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/17b209864395fd7419a0bb22370cc4839d85d115db9a1f1c7a5947236dca548b/hostname",
        "HostsPath": "/var/lib/docker/containers/17b209864395fd7419a0bb22370cc4839d85d115db9a1f1c7a5947236dca548b/hosts",
        "LogPath": "/var/lib/docker/containers/17b209864395fd7419a0bb22370cc4839d85d115db9a1f1c7a5947236dca548b/17b209864395fd7419a0bb22370cc4839d85d115db9a1f1c7a5947236dca548b-json.log",
        "Name": "/intelligent_lichterman",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "CgroupnsMode": "host",
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": true,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/4f563f3f33e7de6d94a8297c9e952819bcc29b2e911e089512c351c3d71a8edb-init/diff:/var/lib/docker/overlay2/fc498dec72595aaaaa87c596f8320732a801f23b9f8f3c584c84bcf377ce2949/diff:/var/lib/docker/overlay2/6bb5823617cec58f587704068adac52dbfd924cc25b13a0bb1213cc7779653b7/diff:/var/lib/docker/overlay2/cb6538f91de4af5307be08d8f4d51611075e24e1c86a51c6640e40d91f955db9/diff:/var/lib/docker/overlay2/1d72b306ec40f2bbcc0555957984ff6d6b08be172075c4a35498a6794ec9fb1d/diff",
                "MergedDir": "/var/lib/docker/overlay2/4f563f3f33e7de6d94a8297c9e952819bcc29b2e911e089512c351c3d71a8edb/merged",
                "UpperDir": "/var/lib/docker/overlay2/4f563f3f33e7de6d94a8297c9e952819bcc29b2e911e089512c351c3d71a8edb/diff",
                "WorkDir": "/var/lib/docker/overlay2/4f563f3f33e7de6d94a8297c9e952819bcc29b2e911e089512c351c3d71a8edb/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [
            {
                "Type": "volume",
                "Name": "8fb68385ed20365b8f99b72948a3b6499d2e22a3c2618d55da11be0ae7ef7eda",
                "Source": "/var/lib/docker/volumes/8fb68385ed20365b8f99b72948a3b6499d2e22a3c2618d55da11be0ae7ef7eda/_data",
                "Destination": "/hello",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            },
            {
                "Type": "volume",
                "Name": "a2623f9b6b97c28063099f378bfd374ee495ac6e183ae10169966f3e8bb6f4c9",
                "Source": "/var/lib/docker/volumes/a2623f9b6b97c28063099f378bfd374ee495ac6e183ae10169966f3e8bb6f4c9/_data",
                "Destination": "/app",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
        "Config": {
            "Hostname": "17b209864395",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "ping baidu.com"
            ],
            "Image": "c84867879973",
            "Volumes": {
                "/app": {},
                "/hello": {}
            },
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "3afb2f26675442701adb5ee23dfb1e1649d0227c3ee1edc689266cfd2455a944",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "/var/run/docker/netns/3afb2f266754",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "581beda59106ff764e60cf08ab088a080f2bfefee8d348e785d1eebb9d2dc012",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "d9bc9bf896664b970195e03de603c983fb9983dcf6b987cede491d338b41d193",
                    "EndpointID": "581beda59106ff764e60cf08ab088a080f2bfefee8d348e785d1eebb9d2dc012",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]

[root@localhost hzh]# cd /var/lib/docker/volumes/8fb68385ed20365b8f99b72948a3b6499d2e22a3c2618d55da11be0ae7ef7eda/_data
[root@localhost _data]# ll
总用量 4
-rw-r--r--. 1 root root 5 1月  12 18:06 a.txt

# 可以看到声明之后确实没有生效
[root@localhost _data]# cat a.txt
1111
[root@localhost _data]# vim a.txt
[root@localhost _data]# cat a.txt
1111
2222
[root@localhost _data]# docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED              STATUS              PORTS     NAMES
17b209864395   c84867879973   "/bin/sh -c 'ping ba…"   About a minute ago   Up About a minute             intelligent_lichterman

[root@localhost _data]# docker exec -it 17b209864395 /bin/sh
/ # cd /hello

/hello # ls
a.txt
/hello # cat a.txt
1111
2222

8、USER

写法:

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

9、EXPOSE

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

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

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

10、multi-stage builds

1、使用

多阶段构建官方文档

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

### 我们如何打包一个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;第二阶段并不会有第一阶段哪些没用的层
#基础镜像没有 jmap; jdk springboot-actutor(jdk)
FROM openjdk:8-jre-alpine
LABEL maintainer="851448443@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=""
# 运行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="851448443@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
# 启动java的命令
ENV JAVA_OPTS=""
ENV PARAMS=""
ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom
$JAVA_OPTS -jar /app.jar $PARAMS" ]

11、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]

12、springboot java 最终写法

FROM openjdk:8-jre-alpine
LABEL maintainer="851448443@qq.com"
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

文章作者: 天际星空
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 天际星空 !
评论
  目录