Docker入门实践笔记-Dockerfile

镜像是一个打包文件,其中包含了应用程序及其运行所依赖的环境,例如文件系统、环境变量、配置参数等等

联合文件系统

容器镜像内部并不是一个平坦的结构,而是由许多的镜像层组成,每层都是只读不可修改修改的一组文件,相同的层可以在镜像之间共享,然后多个层像搭积木一样堆叠起来,使用一种叫"Union FS联合文件系统"的技术将它们合并在一起,形成了容器最终看到的文件系统

img

使用docker inspect可查看镜像的分层信息

$ sudo docker inspect nginx:alpine
...
"RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:ec34fcc1d526fba48f7f88e4ec765fccc17d4692570db85cf32d9d6b020330f2",
                "sha256:5c497738d8721551c4835d7860e90259e10a1396c988377654fdfa775bf6f3a5",
                "sha256:a2a3844a950f5f1872001e95f272cf298f60e5afdea9ac5456be96283038f972",
                "sha256:6dec34c67396c2da53c440189accfc7359b6d6f29f1fe028c056f75f2c3b51a4",
                "sha256:5b75fcec28b0cfd2ea2143d9de81409e98f4ed2c396877b45522919e0821bea0",
                "sha256:7d4368f5306ea780e22b1bc0d0960ba2a5b84b7488d9e2eaacdfcabffd10ca35"
            ]
        },
...

Dockerfile

Dockerfile是一个纯文本,里面记录了一系列构建指令,比如选择基础镜像、拷贝文件、运行脚本等等,每个指令都会生成一个Layer,而Docker顺序执行这个文件里的所有步骤,最后创建出一个新的镜像

创建一个最简单的Dockerfile:

FROM busybox
CMD echo "hello world"

该文件里仅有两条指令,第一条指令是FROM,所有的Dockerfile都要从它开始,表示选择构建使用的基础镜像

第二条指令是CMD,指定了docker run启动容器时默认运行的命令,这里使用echo命令,输出字符串"hello world"

使用docker build命令创建出镜像:

$ sudo docker build -f Dockerfile.busybox .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM busybox
 ---> 62aedd01bd85
Step 2/2 : CMD echo "hello world"
 ---> Running in 6047b8d8741c
Removing intermediate container 6047b8d8741c
 ---> 35c82ebb67d7
Successfully built 35c82ebb67d7

命令的格式: 用-f参数制定Dockerfile文件名,后面必须跟一个文件路径,叫做"构建上下文",这里使用了点号,表示当前路径

$ sudo docker run 35c
hello world

新的镜像暂时没有名字,使用"IMAGE ID"可以指定它,如上通过指定路径前缀运行容器

Docker会逐行地读取并执行Dockerfile里的指令,依次创建镜像层,再生成完整的镜像

简单编写方法

构建镜像的第一条指令必须是FROM,所以基础镜像的选择非常关键,关注镜像的安全性和大小的话,一般选择Alpine,如果关注的是应用的运行稳定性,那么可能会选择Ubuntu、Debian、CentOS

FROM alpine:3.15      # 选择Alpine镜像
FROM ubuntu:bionic    # 选择Ubuntu镜像

COPY命令可以将文件放到容器中,但源文件路径必须是"构建上下文"路径下

COPY ./a.txt /tmp/a.txt   # 将构建上下文中的a.txt拷贝到镜像的/tmp目录里

Dockerfile中最重要的指令是RUN,它可以执行任意shell命令,比如更新系统、安装应用、下载文件、创建目录、编译程序等等,实现任意的镜像构建步骤,其中一条指令只能是一行,所以有的RUN指令会在每行的末尾使用续行符,命令之间也会用&&来连接,如下所示:

RUN apt-get update \
    && apt-get install -y \
        build-essential \
        curl \
        make \
        unzip \
    && cd /tmp \
    && curl -fSL xxx.tar.gz -o xxx.tar.gz\
    && tar xzf xxx.tar.gz \
    && cd xxx \
    && ./config \
    && make \
    && make clean

也可以将这些Shell命令集中到一个脚本文件里,用COPY命令拷贝进去再用RUN执行:

COPY setup.sh  /tmp/                # 拷贝脚本到/tmp目录

RUN cd /tmp && chmod +x setup.sh \  # 添加执行权限
    && ./setup.sh && rm setup.sh    # 运行脚本然后再删除

RUN指令实际上就是Shell编程,使用ARG和ENV可以创建变量,区别在于ARG创建的变量只在镜像构建过程中可见,容器运行时不可见,而ENV创建的变量不仅能够在构建镜像的过程中使用,在容器运行时也能够以环境变量的形式被应用程序使用

ARG IMAGE_BASE="node"
ARG IMAGE_TAG="alpine"

ENV PATH=$PATH:/tmp
ENV DEBUG=OFF

还有一个重要的指令是EXPOSE,它用来声明容器对外服务的端口号

EXPOSE 443
EXPOSE 53/udp

每个指令都会生成一个镜像层,所以Dockerfile里最好不要滥用指令,尽量精简合并,否则太多的层会导致镜像臃肿不堪

posted @ 2022-07-20 09:13  N3ptune  阅读(348)  评论(0编辑  收藏  举报