docker-compose --build 需要永远,占用大量空间

如何解决docker-compose --build 需要永远,占用大量空间

问题(2):

执行 docker-compose --builddocker-compose up --build 会导致所有容器的构建时间都非常长。此外,docker 似乎正在 /tmp 中创建一个非常大的 tmp 文件,我相信这是写入此文件的当前目录。

疑难解答:

  • 尝试将 docker 升级到 20.10.6
  • 执行 watch df -H 显示主分区填满,然后系统什么也不做(docker-compose --build 进程挂起,什么也没有发生)。
  • 我执行了 docker system prune -a,删除了大约 50GB。
  • 主机为 / 设置了 200GB 分区,这是所有 docker 卷所在的位置。我将此分区扩展到 500GB,并尝试再次构建,此时第一个容器映像最终构建(耗时 20 分钟)。之后,下一个映像开始,这也需要 20 分钟来构建每个其他容器,等等,构建所有内容需要大约 2 小时。
  • 我找到了空间枯竭的根源。在 /tmp 中有一个名为“tmpsx30lncc”的文件(我假设最后是随机字符),它的大小正在增长并且与构建过程中消耗的大小相匹配。运行 file tmpsx30lncc 显示其 POSIX tar 存档。我在网上读到这个文件是我当前运行 docker-compose build 的目录的存档。传输速率似乎有所不同。根据观察文件增加的情况,它大约是每秒 50-250MB。
  • 在某些时候(我相信当所有内容都归档到 /tmp 中的 tmp 归档文件时,df -H 显示返回的大小很慢,而每个容器都进入了它的构建步骤。
  • 我做了一个 du --block-size=M -a / | sort -n -r | head -n 20,大文件的罪魁祸首是 /tmp 文件(运行 docker-compose 的目录的完整大小),我的 docker-registry 文件夹(~70GB),最后是 gitea(4GB) ),其他容器也有其他文件夹。 docker-compose 复制所有文件的主目录约为 76GB。

系统

  • 操作系统:Ubuntu 18.04
  • Docker 版本:19.03.3
  • 尝试在 docker-compose 中构建的容器:gitea、postgres、drone、drone-runner、docker-registry
  • volumes:都位于我/分区的不同位置。 Gitea 将在某个时候构建,但下一个图像将表现出相同的行为,并花费同样的时间并填满硬盘驱动器。

码头信息:

    Client:
     Context:    default
     Debug Mode: false
     Plugins:
      app: Docker App (Docker Inc.,v0.9.1-beta3)
      buildx: Build with BuildKit (Docker Inc.,v0.5.1-docker)
      scan: Docker Scan (Docker Inc.,v0.7.0)
    
    Server:
     Containers: 0
      Running: 0
      Paused: 0
      Stopped: 0
     Images: 0
     Server Version: 19.03.3
     Storage Driver: overlay2
      Backing Filesystem: extfs
      Supports d_type: true
      Native Overlay Diff: true
     Logging Driver: json-file
     Cgroup Driver: cgroupfs
     Plugins:
      Volume: local
      Network: bridge host ipvlan macvlan null overlay
      Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
     Swarm: inactive
     Runtimes: runc
     Default Runtime: runc
     Init Binary: docker-init
     containerd version: 05f951a3781f4f2c1911b05e61c160e9c30eaa8e
     runc version: 12644e614e25b05da6fd08a38ffa0cfe1903fdec
     init version: fec3683
     Security Options:
      apparmor
      seccomp
       Profile: default
     Kernel Version: 4.15.0-141-generic
     Operating System: Ubuntu 18.04.5 LTS
     OSType: linux
     Architecture: x86_64
     CPUs: 10
     Total Memory: 15.64GiB
     Name: taxmd01-gitjenkins-v
     ID: ZZYO:KANZ:3AH3:UO4U:HFHC:G6OW:4UFQ:S7Q5:QNVC:O5QH:33P7:MRMN
     Docker Root Dir: /var/lib/docker
     Debug Mode: false
     Registry: https://index.docker.io/v1/
     Labels:
     Experimental: false
     Insecure Registries:
      127.0.0.0/8
     Live Restore Enabled: false

如何复制

  • 使用与上述相同的系统和版本
  • 这是我的 docker-compose.yaml(我已经调整了 IP、域、密码和机密),要使其正常工作,您需要调整 IP 的端口、证书和目录结构。
  • 在 ubuntu 中使用 netplan 在一个接口上配置所有 IP 地址
  • 要部署重新创建的目录结构,并在与下面的 docker-compose.yaml 文件相同的目录中运行 docker-compose build。我担心如果您真的正确设置了所有内容,您可能不会遇到与我相同的问题,因为随着时间的推移,我的图像/体积会非常大。
    version: "3.7"
    
    services:
      gitea:
        build:
          context: ./
          dockerfile: services/gitea/gitea.Dockerfile
        volumes:
          - ./services/gitea/data:/data
          - /etc/timezone:/etc/timezone:ro
          - /etc/localtime:/etc/localtime:ro
        ports:
          - 10.10.10.31:22:22
          - 10.10.10.31:80:3080
          - 10.10.10.31:443:3443
        environment:
          - USER_UID=1000
          - USER_GID=1000
          - DB_TYPE=postgres
          - DB_HOST=postgres:5432
          - DB_NAME=myDBname
          - DB_USER=myUser
          - DB_PASSWD=myPassword
      postgres:
        image: postgres:latest
        volumes:
          - ./services/postgres/data:/var/lib/postgresql/data
        environment:
          - POSTGRES_USER=myUser
          - POSTGRES_PASSWORD=myPassword
          - POSTGRES_DB=myDBname
      drone:
        build:
          context: ./
          dockerfile: services/drone/drone.Dockerfile
        ports:
          - 10.10.10.28:80:80
          - 10.10.10.28:443:443
        volumes:
          - ./services/drone/data:/data
        environment:
          DRONE_GITEA_SERVER: "https://gitea2.example.internal"
          DRONE_GITEA_CLIENT_ID: 023f302d-ffff-22b3-83fc-898d9b68fbe1
          DRONE_GITEA_CLIENT_SECRET: MySecretab897a797c979d79779e79f
          DRONE_USER_CREATE: username:drone-admin,machine:false,admin:true,token:MyToken3892183902183902183012
          DRONE_SERVER_PROTO: https
          DRONE_SERVER_HOST: "drone2.example.internal"
          DRONE_RPC_SECRET: MyRPCSecretab897a797c979d79779e79f
          DRONE_TLS_CERT: /cert/drone.crt
          DRONE_TLS_KEY: /cert/drone.key
          DRONE_LOGS_DEBUG: "false"
          DRONE_LOGS_TRACE: "false"
      drone-runner:
        build:
          context: ./
          dockerfile: services/drone-runner/drone-runner.Dockerfile
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
        environment:
          DRONE_RPC_PROTO: https
          DRONE_RPC_HOST: drone
          DRONE_RPC_SECRET: MyRPCSecretab897a797c979d79779e79f
          DRONE_RUNNER_CAPACITY: 4
          DRONE_RUNNER_NAME: drone-runner
          DRONE_RPC_DUMP_HTTP: "false"
          DRONE_RPC_DUMP_HTTP_BODY: "false"
      docker-registry:
        build:
          context: ./
          dockerfile: services/docker-registry/docker-registry.Dockerfile
        ports:
          - 10.10.10.27:443:443
        volumes:
          - ./services/docker-registry/data:/var/lib/registry
        environment:
          REGISTRY_HTTP_ADDR: 0.0.0.0:443
          REGISTRY_HTTP_TLS_CERTIFICATE: /cert/docker-registry.crt
          REGISTRY_HTTP_TLS_KEY: /cert/docker-registry.key

这里是每个 docker 文件:

srv/ci/services# cat gitea/gitea.Dockerfile
from gitea/gitea:latest
copy ./ca/* /usr/local/share/ca-certificates/
run update-ca-certificates
copy ./services/gitea/cert /cert
run chmod 700 /cert && chown -R myUser:myUser /cert

/srv/ci/services# cat drone/drone.Dockerfile
from drone/drone:1
run apk update && apk add --no-cache ca-certificates
copy ./ca/ca.crt /usr/local/share/ca-certificates/
run update-ca-certificates
copy ./services/drone/cert /cert
run chmod 700 /cert

/srv/ci/services# cat drone-runner/drone-runner.Dockerfile
from drone/drone-runner-docker:1
run apk update && apk add --no-cache ca-certificates
copy ./ca/ca.crt /usr/local/share/ca-certificates/
run update-ca-certificates

/srv/ci/services# cat docker-registry/docker-registry.Dockerfile
from registry
run apk update && apk add --no-cache ca-certificates
copy ./ca/ca.crt /usr/local/share/ca-certificates/
run update-ca-certificates
copy ./services/docker-registry/cert /cert/
run chmod 700 /cert

这是目录结构

enter image description here

所以请原谅我,我不是 docker 专家,而且我最初没有构建这个系统,它用于我们的开发团队。该系统已经存在 2 年了,并且在空间利用率方面有了很大的提高。我只能假设问题是容器的数据库被填满了,或者 docker-registry 有很多图像占用了太多空间。

问题:

  • 为什么我的每个容器都需要 20 分钟来构建? (我假设因为它需要复制位于 docker-compose 文件中的所有文件。)
  • 为什么我的 /tmp 目录中有这么大的 tmp 文件,为什么我的系统传输到这个文件的速度这么慢?有没有办法阻止 docker 创建这个文件?
  • 关于如何减少这些长得离谱的构建时间有什么建议吗?

解决方法

Docker 使用客户端/服务器模型。你实际上并不直接启动容器或构建镜像;相反,您与 Docker 守护进程建立 HTTP 连接(通常通过 Unix 套接字),并指示它做什么。

更具体地说,当您构建映像时,Docker 守护程序不会直接读取文件系统。相反,docker builddocker-compose build 都会创建上下文目录的 tar 文件,并将其作为 HTTP POST 请求的正文发送以启动构建。当 Docker 守护进程接收到文件时,它需要将它缓冲在磁盘上,然后解压它才能真正开始构建。

您提到,在 build: {context: } 目录的子目录中,您有几个大数据目录(一个应用程序为 70 GB,另一个应用程序为 4 GB)。即使此内容实际上并未在构建中使用,它仍会经历此网络传输过程(即使在本地系统上)。这需要时间通过套接字传输(移动 75 GB 需要 20 分钟是可信的)并且需要保存和解压缩(您看到的 /tmp 中的非常大的文件)。

您可以创建一个 .dockerignore file 来命名需要从上下文中省略的文件。这需要在 context: 目录中,即使 dockerfile: 在其他地方。使用您显示的目录结构,您可以省略所有 data 目录:

# /srv/ci/Dockerfile
services/*/data

与此相反的是,您包含在 .dockerignore 中的文件无法COPY放入图像中。这只影响镜像构建过程,对volumes:没有影响;您可以在运行时将省略的目录绑定挂载到容器中,即使它不应该包含在图像中。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?