发布于 2026-01-05 1 阅读
0

CI/CD 流水线中快速构建 Docker 镜像的技巧 DEV's Worldwide Show and Tell Challenge Presented by Mux: Pitch Your Projects!

如何在 CI/CD 流水线中(快速)构建 Docker 镜像的技巧

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

Docker 镜像的构建技术近年来不断发展,涌现出了许多新的策略。其中一个常见的痛点是 CI/CD 流水线中镜像构建速度缓慢。许多流水线本身就是 Docker 容器,完全处于待优化状态。如果我们没有意识到这一点,就会错失许多优化机会。

以下是一些技巧和窍门,可以帮助您加快 Docker 镜像在构建流水线中的运行速度。我曾见过构建时间从几十分钟缩短到不到 60 秒,不过实际情况可能因人而异!

  • 用法.dockerfileignore:每次 Docker 构建的第一步都是 Docker 将整个目录打包成 tar 包,并将数据发送到 Docker 服务器。输出结果会显示为一行。这Sending build context to Docker daemon XX.XXMB应该只包含构建过程中使用的源代码和配置文件。它的大小不应该超过几十 MB。请务必排除所有不需要的文件。
  • 不常更改的内容应该放在 Dockerfile 的顶部。例如,初始安装的软件包和静态环境变量都应该移到顶部。Docker 有缓存机制,但如果 Dockerfile 中第一行发生更改,之后的所有行都不会再被缓存。Dockerfile 的最后一行应该是添加你的代码,或者编译它!

一个典型的文件应该遵循以下流程:

  FROM base-cotnainer

  # Add all static environment variables, folders, users...
  ENV IS_APPLICATION=1
  RUN mkdir app
  WORKDIR /app  
  RUN useradd -ms /bin/sh newuser
  COMMAND ["python3", "main.py"]

  # Install all base image packages
  RUN apt install ...

  # Copy over your library depency files (Python example below)
  COPY Pipfile /app/Pipfile
  COPY Pipfile.lock /app/Pipfile.lock

  # Install your libraries (Python example below)
  RUN pipenv install --deploy

  # Lastly, copy the code over
  COPY . /app/
Enter fullscreen mode Exit fullscreen mode
  • 命令中不要使用通配符COPY。Docker 永远不会缓存该行命令。请务必明确指定!

  • 容器只需构建一次。没有任何理由需要多次构建应用程序。

  • 同时构建所有容器。如果您有应用程序容器、测试容器、集成容器、性能容器等等,
    请将它们作为并行任务/步骤/作业添加到您的流水线中。Docker 构建的大部分操作都与 I/O 任务相关,这些任务可以而且应该并行化。

  • 手动复制库依赖项文件定义,然后在复制源代码之前运行库安装程序!
    先执行此步骤可以缓存库的拉取过程(这感觉就像从互联网完整下载了一遍)。以下是
    针对特定语言的示例:

    • Javascript
      FROM [...]
      mkdir /app
      WORKDIR /app
      # ...
      COPY package.json /app
      COPY package-lock.json /app
      RUN npm install
    
      COPY [all your code and configuration]    
    
    • Python(requirements.txt)
    FROM [...]
    mkdir /app
    WORKDIR /app
    # ...
    COPY requirements.txt /app
    RUN pip3 install
    
    COPY [all your code and configuration]    
    
    • Python(pipenv)
    FROM [...]
    mkdir /app
    WORKDIR /app
    # ...
    COPY Pipfile /app
    COPY Pipfile.lock /app
    RUN pipenv install --deploy
    
    COPY [all your code and configuration]    
    
    • Java(Maven)
    FROM [...]
    mkdir /app
    WORKDIR /app
    # ...
    COPY pom.xml /app/pom.xml
    RUN mvn install
    
    COPY [all your code and configuration]    
    RUN mvn package
    
    • Java(Gradle)

    请将以下内容添加到您的build.gradle文件中:

    task downloadDependencies(type: Exec) {
    configurations.testRuntime.files
    commandLine 'echo', 'Downloaded all dependencies'
    }
    

    示例 Dockerfile,用于将依赖项作为缓存层拉取。

    FROM [...]
    mkdir /app
    WORKDIR /app
    # ...
    COPY build.gradle /app/build.gradle
    RUN ./gradlew downloadDependencies
    
    COPY [all your code and configuration]    
    RUN ./gradlew build
    
    • .NET(Nuget)
    FROM [...]
    mkdir /app
    WORKDIR /app
    # ...
    # Copy all solution files .sln (NO WILDCARDS!)
    COPY WebApp.sln /app/WebApp.sln 
    # COPY all .csproj files (NO WILDCARDS!)
    COPY WebApp.Application/WebApp.Application.csproj /app/WebApp.Application/WebApp.Application.csproj
    # Yep.. gotta manually add them all (... Pokemon!)
    
    # This command will pull all library 
    RUN dotnet restore
    
    COPY [all your code and configuration]    
    
    • 不要预先拉取镜像。这样做速度慢而且无效。使用BUILDKIT下面定义的新方法,Docker 会更智能地只拉取已缓存的镜像层。这样速度更快、效率更高,而且有效。
  • 在构建过程中使用 Docker 的新库BUILDKIT并将内联缓存信息集成到构建流程中。(这看似简单,实则意义重大)
    要在流水线中实现正确的镜像缓存,我们需要设置以下三项:

    1. 将环境变量设置DOCKER_BUILDKIT为 1
    2. 添加一个构建参数,告诉新的 BUILDKIT 库在每个图像层中写入缓存信息:--build-arg BUILDKIT_INLINE_CACHE=1
    3. 告诉 Docker 构建命令从哪里获取镜像以进行缓存。--cache-from [remote-repo-url]/[CONTAINER_NAME]:latest完整的命令类似于:DOCKER_BUILDKIT=1 docker build --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from [remote-repo-url]/[CONTAINER_NAME]:latest--tag [CONTAINER_NAME] 。

最后,别忘了将更新后的镜像推送到远程 Docker 仓库!

文章来源:https://dev.to/stevesmashcode/tips-on-building-a-docker-image-quickly-in-a-ci-cd-2ph7