为什么主机的行为可能比 docker 容器更具确定性?

Gis*_*zmo 5 docker

我们使用 Docker 来很好地定义构建环境并帮助确定性构建,但在我的机器上,我使用 Docker 的构建结果发生了微小的变化,但不使用 Docker 时则没有。

我做了相当广泛的测试并且没有想法:(

我在以下系统上进行了测试:

  • A : 我的新 PC 没有 Docker
  • AD1:我的新 PC 和 Docker,使用基于 ubuntu:18.04 的 Dockerfile,“一年前”编译
  • AD2:我的新 PC 和 Docker,使用我们基于 ubuntu:19:10 的 Dockerfile 现在编译
  • B : 我的笔记本电脑(我已经将磁盘复制到我的新电脑上)没有 Docker
  • BD : 我的带 Docker 的笔记本电脑
  • CD1:同事的笔记本电脑上的 Docker,使用我们基于 ubuntu:18.04 的 Dockerfile,“一年前”编译
  • CD2 : 同事的笔记本电脑和 Docker,使用我们基于 ubuntu:19:10 现在编译的 Dockerfile
  • DD : 一个基于 ubuntu:18.04 现在编译的带有 Dockerfile 的 Digital Ocean VPS

在所有场景中,我们都得到了两个构建结果中的任何一个,我将命名变体 X 和 Y。

  • 我们使用 A、B、CD1、CD2 和 DD 得到了变体 X。
  • 我们使用 AD1、AD2 和 BD 得到了变体 Y。

自我们的 Android 应用程序发布多个版本以来,该问题一直可以 100% 重现。当我将 Docker 从 19.03.6 更新到 19.03.8 以匹配我同事的版本时,它并没有消失。那时我们都有 Ubuntu 19.10,而我现在不断遇到 Ubuntu 20.04 的问题。

我总是将我们的项目新克隆到一个新文件夹中,使用disorderfs 来消除文件系统排序问题并将文件夹安装到docker 容器中。

我怀疑它是否相关,但我们正在使用这个 Dockerfile:

FROM ubuntu:18.04

RUN dpkg --add-architecture i386 && \
    apt-get update -y && \
    apt-get install -y software-properties-common && \
    apt-get update -y && \
    apt-get install -y wget \
            openjdk-8-jre-headless=8u162-b12-1 \
            openjdk-8-jre=8u162-b12-1 \
            openjdk-8-jdk-headless=8u162-b12-1 \
            openjdk-8-jdk=8u162-b12-1 \
            git unzip && \
    rm -rf /var/lib/apt/lists/* && \
    apt-get autoremove -y && \
    apt-get clean

# download and install Android SDK
ARG ANDROID_SDK_VERSION=4333796
ENV ANDROID_HOME /opt/android-sdk
RUN mkdir -p /opt/android-sdk && cd /opt/android-sdk && \
    wget -q https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_VERSION}.zip && \
    unzip *tools*linux*.zip && \
    rm *tools*linux*.zip && \
    yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses
Run Code Online (Sandbox Code Playgroud)

另外这里是构建说明我运行,并得到不同的结果。差异本身可以在这里找到。


编辑:我也将其作为docker repo 上的错误提交。

Zby*_*zek 1

Docker 并不完全独立于架构。对于不同的架构,可能会有或多或少的细微差别。通常它不应该影响任何重要的事情,但可能会改变编译器等的一些优化决策。如果您尝试不同的 CPU(例如 AMD64 与 ARM),这一点会更加明显。对于 Java 来说这应该不重要,但似乎至少有时它很重要。

另一件事是网络和 DNS。当你执行 apt-get、wget 和其他类似操作时,它会从网络下载代码或二进制文件。它可能会有所不同,具体取决于您使用的 DNS(这可能会导致不同的服务器或不同的存储库 URL),并且可能存在一些细微的差异。理论上应该没有区别,但实际上有时可能会有区别,比如当他们推出新版本时,它仅在某些节点上可见,或者发生了一些不好的事情,或者您之间有一些缓存/代理并通过它进行连接,它会缓存等。

后者也会造成随时间出现的差异。就像应用程序是在一个月内编译的,有人试图在几周或几个月后验证,然后 apt-get 安装其他版本的库,实际上存在微小的差异。

我不确定这适用于这里,但我有一些想法:

  • 可能会尝试对应用程序进行一些小的更改,因此实际上它将再次在大多数流行的 CPU 上构建相同的版本,进行广泛的测试,然后列出可以验证它的架构

  • 使验证过程变得更加复杂且非免费,因此用户应该必须运行具有指定架构的服务器实例(在 AWS 或 Google 或 Azure 或 Rackspace 或其他上)并在那里构建和验证 - 可以尝试指定在哪些类型的机器上确切的结果将是相同的,最低要求是什么(因为它可能会也可能不会在免费计划实例上运行)

  • 检查创建的镜像内容的差异(不仅是 apk,还包括完整的系统镜像),也许不同机器上的 docker 镜像之间存在一些重要的差异,产生不同的结果

  • 尝试找到尽可能小的初始映像,并且不允许 apt-get 或其他东西自动安装最新版本的依赖项,但指定所有依赖项及其版本