Dockerfile编写指南

红米2年前技术文章777

一、背景


  在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分钟以上,所以在可以选择的情况下,尽可能的选择方案二。


          相关文章

          Redis 持久化机制 RDB

          Redis 持久化机制 RDB

          前言Redis 有两种持久化机制,分别是 RDB 与 AOF 本篇文章将介绍 RDB 的执行过程与应用。1. RDB 简介RDB 持久化是把当前进程数据生成快照保存到硬盘的过程,触发 RDB 持久化过...

           oracle11g打补丁31718723报错Operation not permitted

          oracle11g打补丁31718723报错Operation not permitted

          oracle11g 在打gi补丁的时候报错如下:原因:参考mos(Doc ID 2668094.1)可知因为Linux/Unix平台上,安装后一些Oracle可执行文件的权限需要修改成root。这是以...

          大数据集群二次开发及调优使用指导(三)-Hive

          大数据集群二次开发及调优使用指导(三)-Hive

          1.   业务调优:Hive业务的业务主要以批量处理作业为主,批处理主要特点是耗时时间长,消耗的资源比较多,主要的调优和设计推荐如下:1.   &nb...

          k8s service IP不能ping通?

          k8s service IP不能ping通?

          1、先看下serviceIP是怎么来的?serviceIP是serviceController生成的,参数--service-cluster-ip-range string会配置在controller...

          PG的统计信息(三)

          1.3 数据分布类统计信息1.3.1 pg_stats通过对pg_stats的查询,可以查看每个字段的数据分析统计信息,类似SQL Server的直方图,为优化器选择最佳执行计划提供依据,pg_sta...

          远程DEBUG HADOOP源码方法

          远程DEBUG HADOOP源码方法

          1. 安装IDEA2. 下载hadoop源码,必须与集群服务代码版本一致,否则会导致有的类无法找到3. 将源码导入IDEA工程并完成build4. 点击 菜单栏--运行--编辑配置 进行相关debug...

          发表评论    

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