3. Docker file

3.1. Docker file 分析

Dockerfile是一个文本文件,用于构建生成Docker镜像,其内以一个镜像为基础,在基础镜像之上通过一条条的 指令(Instruction)加工、定制,最终产出自己想要的镜像。每一条指令都基于前一层依赖的镜像层构建一层新的镜像层,因此每一条指令的内容,就是描述该层应当如何构建。

FROM cfp/build as builder

# Set working directory for the build
WORKDIR $GOPATH/src/github.com/fpchan/cfp

# Add cfp source files
COPY . .

# build cfp
RUN make install
RUN make launchcmd

# FROM cfp/node
FROM cfp/build

# Set working directory for the build
WORKDIR $GOPATH/src/github.com/cosmos/launch

ARG LAUNCH_REPOSITORY=https://github.com/cfp/launch
ARG LAUNCH_BRANCH=dev
ARG SEED_NODE_NUM=1
ARG VAL_NODE_NUM=1
ARG FULL_NODE_NUM=1

RUN mkdir -p $GOPATH/src/github.com/cosmos \
    && cd $GOPATH/src/github.com/cosmos \
    && git clone $LAUNCH_REPOSITORY -b $LAUNCH_BRANCH

COPY --from=builder $GOPATH/bin $GOPATH/bin
COPY docker/launch/genfile.sh .
COPY docker/launch/start.sh .

EXPOSE 26656 26657 26658 26659 6060

# generate genesis file
RUN $GOPATH/src/github.com/cosmos/launch/genfile.sh  ${SEED_NODE_NUM} ${VAL_NODE_NUM} ${FULL_NODE_NUM}
ENTRYPOINT $GOPATH/src/github.com/cosmos/launch/start.sh
docker build \
--build-arg SEED_NODE_NUM=1 \
--build-arg VAL_NODE_NUM=4 \
--build-arg FULL_NODE_NUM=10 \
-f Dockerfile -t cfp/node-1:rr .
  • FROM指定基础镜像

  • COPY从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的<目标路径>位置

    • 构建上下文目录是指docker build指定的目录,通常使用.即当前目录,操作文件不可以超出上下文目录

    • COPY [--chown=<user>:<group>] <源路径>... <目标路径>

    • COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]

  • ADDCOPY基本一致,但如果<源路径>是URL或者压缩文件,ADD可以自动下载和解压

  • RUN指定要执行的命令

    • shell 格式:RUN <命令>

    • exec 格式:RUN ["可执行文件", "参数1", "参数2"]

  • CMD启动容器时执行的命令,格式同RUN

    • 在运行时可以替代,docker run中执行指定的指令或docker-compose中的command

    • 在指令格式上,一般推荐使用 exec格式,这类格式在解析时会被解析为 JSON 数组,因此一定要使用双引号 ",而不要使用单引号

  • ENTRYPOINTCMD含义相同,格式同RUN

    • 当同时执行CMDENTRYPOINT后,CMD不再是直接的运行命令,二是将其作为参数传给ENTRYPOINT,变成<ENTRYPOINT> "<CMD>"

    • ENTRYPOINTCMD的优势:

      • 如果指定CMD [ "curl", "-s", "https://ip.cn" ],执行docker run {images}会输出IP,如果想加入参数-i,则执行docker run {images} -i会报错

      • 如果指定ENTRYPOINT [ "curl", "-s", "https://ip.cn" ],同样的执行docker run {images} -i则没问题,相当于执行curl -s https://ip.cn -i

  • ENV环境变量

  • ARG构建参数

  • EXPOSE声明容器提供的服务端口

  • WORKDIR工作目录

  • USER指定当前用户

3.2. Docker file 最佳实践

  • 使用.dockerignore文件

  • 使用多阶段构建

    • 多阶段构建,允许在一个Dockerfile中使用多个FROM指令,可直接将前置阶段、或其他镜像中的资源文件复制到当前构建阶段,并最终只产生最后一个镜像,大大减少了镜像大小及个数

  • 避免安装不必要的包

  • 一个容器只运行一个进程

  • 镜像层数尽可能少

  • 构建缓存

    • 如果你不想在构建过程中使用缓存,你可以在 docker build命令中使用 --no-cache=true选项

  • 将多个 RUN 指令合并为一个

    • Dockerfile 中的每个指令都会创建一个新的镜像层。

    • 镜像层将被缓存和复用

    • 当 Dockerfile 的指令修改了,复制的文件变化了,或者构建镜像时指定的变量不同了,对应的镜像层缓存就会失效

    • 某一层的镜像缓存失效之后,它之后的镜像层缓存都会失效

    • 镜像层是不可变的,如果我们再某一层中添加一个文件,然后在下一层中删除它,则镜像中依然会包含该文件(只是这个文件在 Docker 容器中不可见了)

  • 基础镜像的标签不要用 latest

    • 当镜像更新时,latest 标签会指向不同的镜像,这时构建镜像有可能失败

  • 每个 RUN 指令后删除多余文件

  • 选择合适的基础镜像(alpine 版本最好)

    • alpine 是一个极小化的 Linux 发行版,只有 4MB,这让它非常适合作为基础镜像

  • COPY 与 ADD 优先使用前者

    • COPY指令非常简单,仅用于将文件拷贝到镜像中。ADD相对来讲复杂,可以用于下载远程文件以及解压压缩包

  • 合理调整 COPY 与 RUN 的顺序

    • 应该把变化最少的部分放在 Dockerfile 的前面,这样可以充分利用镜像缓存。