Dockerfile编写指南

红米2年前技术文章624

一、背景


  在k8s提出抛弃docker,拥抱其他的cri插件的情况下,为什么还有这篇指南呢?首先Dockerfile作为容器打包的标准,已经存在了很多年了。其中的编写技巧到现在也是一直延续的,所以并不存在CRI不使用docker了,Dockerfile就无法使用了的问题。其次我们在制作多CPU架构的容器镜像的时候,还是要使用docker去进行打包编译的,所以Dockerfile还是需要继续学习和参考的。

二、构建简单镜像

1、Dockerfile 通过 docker build 命令进行构建

20230413140433.jpg

在执行docker build命令的时候,是通过docker cli把上下文提交给docker daemon去进行处理的基础的Dockerfile

FROM centos
RUN yum -y install wget \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
    && tar -xvf redis.tar.gz

2、需要注意的指令异同

COPY & ADD
copy

复制指令,从上下文目录中复制文件或者目录到容器里指定路径。

格式:

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


[--chown=:]:可选参数,用户改变复制到容器内文件的拥有者和属组。

<源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:

COPY hom* /mydir/
COPY hom?.txt /mydir/


<目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

ADD

ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:

  • ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。

  • ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

    CMD

    类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

    • CMD 在docker run 时运行。

    • RUN 是在 docker build。

      作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。

      注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。

      格式:

      CMD <shell 命令> 
      CMD ["<可执行文件或命令>","<param1>","<param2>",...] 
      CMD ["<param1>","<param2>",...]  # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数

      推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh。

      ENTRYPOINT

      类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。

      但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。

      优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

      注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

      格式:

      ENTRYPOINT ["<executeable>","<param1>","<param2>",...]

      可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,以下示例会提到。

      示例: 假设已通过 Dockerfile 构建了 nginx:test 镜像:

      FROM nginx
      
      ENTRYPOINT ["nginx", "-c"] # 定参
      CMD ["/etc/nginx/nginx.conf"] # 变参

      1、不传参运行

      $ docker run  nginx:test

      容器内会默认运行以下命令,启动主进程。

      nginx -c /etc/nginx/nginx.conf

      2、传参运行

      $ docker run  nginx:test -c /etc/nginx/new.conf

      容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)

      nginx -c /etc/nginx/new.conf
      ENV

      设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

      格式:

      ENV <key> <value>
      ENV <key1>=<value1> <key2>=<value2>...

      以下示例设置 NODE_VERSION = 7.2.0 , 在后续的指令中可以通过 $NODE_VERSION 引用:

      ENV NODE_VERSION 7.2.0
      
      RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
        && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
      ARG

      构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。

      构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。

      格式:

      ARG <参数名>[=<默认值>]
      VOLUME

      定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。

      作用:

      • 避免重要的数据,因容器重启而丢失,这是非常致命的。

      • 避免容器不断变大。

        格式:

        VOLUME ["<路径1>", "<路径2>"...]
        VOLUME <路径>

        在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。

        EXPOSE

        仅仅只是声明端口。

        作用:

        • 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。

        • 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

          格式:

          EXPOSE <端口1> [<端口2>...]
          WORKDIR

          指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)。

          docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。

          格式:

          WORKDIR <工作目录路径>
          USER

          用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。

          格式:

          USER <用户名>[:<用户组>]

          三、构建多CPU架构镜像

          1、利用buildx直接构建多CPU架构镜像

          # 需要提前安装QEMU,下面是直接把镜像打包成 Linux/amd64和Linux/arm64 架构,需要注意基础镜像要支持多CPU架构
          $ docker buildx build --platform linux/amd64,linux/arm64 .

          2、利用交叉编译,多阶段打包,buildx 构建多CPU架构镜像

          # 需要是支持交叉编译的编程语言,例如:C++、go
          $  cat Dockerfile 
          # Build the manager binary
          FROM --platform=${BUILDPLATFORM} golang:1.17-alpine as builder # 这里通过platform引用buildx时传入的CPU架构参数
          
          ARG TARGETOS      # 通过上面承接的变量自动获取到OS类别
          ARG TARGETARCH    # 通过上面承接的变量自动获取到CPU架构
          
          ARG goproxy=https://goproxy.cn,direct
          ENV GOPROXY=${goproxy}
          WORKDIR /workspace
          # Copy the Go Modules manifests
          COPY go.mod go.mod
          COPY go.sum go.sum
          # cache deps before building and copying source so that we don't need to re-download as much
          # and so that source changes don't invalidate our downloaded layer
          RUN go mod download
          
          # Copy the go source
          COPY main.go main.go
          COPY apis/ apis/
          COPY controllers/ controllers/
          
          # Build
          RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -a -o flink-operator main.go #把OS以及CPU架构传给打包的go命令
          
          # Use distroless as minimal base image to package the manager binary
          # Refer to https://github.com/GoogleContainerTools/distroless for more details
          FROM ubuntu:latest AS ubuntu    # 通过多阶段镜像构建,只拷贝上个阶段构建的二进制程序,减小最终镜像的大小
          WORKDIR /
          COPY --from=builder /workspace/flink-operator .  # 此步骤就是从上个阶段拷贝打包出来的go 二进制程序
          USER 65532:65532
          
          ENTRYPOINT ["/flink-operator"]
          
          $ docker buildx build --platform=linux/amd64,linux/arm64 -f Dockerfile  -t harbor.dtsre.com/flink/flink-operator:1.0.0 \
           . --push
          思考:在有第一种方案的基础上,为什么还会有第二种方案呢?

          答:第一种方案是比较简单粗暴的,利用的是不同CPU架构的虚拟机来进行镜像制作的,但是有一个非常大的弊端:效率太低了,打包变的非常的慢,每次打包都要20分钟以上,所以在可以选择的情况下,尽可能的选择方案二。


          相关文章

          MySQL性能优化(六)优化or条件

          MySQL性能优化(六)优化or条件

          优化器是数据库中非常核心,又非常复杂的一个组件。有的SQL,优化器选择的执行计划并不是最优的,通过改写SQL,可以帮助优化器找到最优的执行计划。where条件中的or子句,是比较容易出问题的一个场景。...

          scylladb通过扩缩容节点迁移数据

          环境: Scyllsdb版本:4.2一、上线新节点1、确认集群状态和检查配置· 首先确认集群各节点状态是Up Normal (UN),[root@172-16-121-153 scylla]# nod...

          mysql binlog文件删除情况下恢复主从

          mysql binlog文件删除情况下恢复主从

          1、对主库的数据进行全库dump2、删除自建的从库数据库3、将主库数据source到从库(1)将全库dump文件传到从库服务器节点scp ./backup_2023-12-08_14_49_17.sq...

          MySQL 使用开源审计插件

          MySQL 使用开源审计插件

          前言MySQL 只有企业版有审计插件,开源社区版没有审计插件。企业要通过等保需要开通审计,这里记录使用 MariaDB 开源审计插件,让 MySQL 社区版拥有审计功能。1. 审计插件下载审计插件是包...

          docker安装及常用操作

          docker安装及常用操作

          一、安装docker1、移除以前docker相关包sudo yum remove docker \      ...

          scylladb简单命令介绍

          一、cql 命令查看版本SHOW VERSION查看连接的数据库地址SHOW HOST批量执行.cql文件中的命令SOURCE '/home/thobbs/commands.cql'开启和关闭命令追踪...

          发表评论    

          ◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。