【问题标题】:Problems building Rust service containers from Docker scratch image从 Docker 临时映像构建 Rust 服务容器的问题
【发布时间】:2022-06-20 19:37:53
【问题描述】:

我有一个想要在容器中运行的 Rust 二进制文件。当我使用ubuntu 甚至gcr.io/distroless/cc 作为基本映像时,它工作正常,但是当我尝试从scratch 映像运行时,我遇到了一些问题。这显然是因为某些文件预计会在不存在的文件系统上。我能够弄清楚如何安装 CA 根证书,但现在我得到了一个不同的错误,当二进制文件尝试进行 DNS 查找时似乎会发生这种错误。

我的 Dockerfile 看起来大多是这样的(我去掉了一些构建优化):

FROM rust:latest as cargo-build

WORKDIR /build
ENV RUSTFLAGS="-C target-feature=+crt-static"

COPY aggregator/ ./

RUN cargo build --release --target x86_64-unknown-linux-gnu

#####################################

FROM alpine:3.6 as ca-certificates
RUN apk add -U --no-cache ca-certificates

#####################################

FROM scratch

COPY --from=cargo-build /build/target/x86_64-unknown-linux-gnu/release/aggregator /entrypoint
COPY --from=ca-certificates /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

ENTRYPOINT [ "/entrypoint" ]

我在运行时遇到的错误(来自 Rust 二进制文件)是:

Error: Failed to load job description from S3

Caused by:
    0: io error: error trying to connect: dns error: Device or resource busy (os error 16)
    1: io error: error trying to connect: dns error: Device or resource busy (os error 16)
    2: error trying to connect: dns error: Device or resource busy (os error 16)
    3: dns error: Device or resource busy (os error 16)
    4: Device or resource busy (os error 16)

我用谷歌搜索了这个错误,并能够找到另一个具有相同错误的 Rust/Docker 项目,但他们通过从 scratch 切换到 gcr.io/distroless/cc 作为基础映像“修复”了问题,我想避免通过了解问题所在。

查看文件系统,它似乎有一个最小的网络配置,但我可能错了。

.
├── dev
│   ├── console
│   ├── pts
│   └── shm
├── entrypoint
├── etc
│   ├── hostname
│   ├── hosts
│   ├── mtab -> /proc/mounts
│   ├── resolv.conf
│   └── ssl
│       └── certs
│           └── ca-certificates.crt
├── proc
└── sys

就像我写的那样,这适用于其他基础映像,那么我缺少什么才能让网络化的 Rust 二进制文件从“零开始”运行?

【问题讨论】:

  • 你不需要用 Alpine 镜像编译你的 Rust 程序,因为你使用 Alpine 来提供其他组件吗?我不认为他们会兼容。
  • 不,它只是一个依赖项查找的文件。 Rust 将 libc 和 openssl 静态编译到二进制文件中,但它不包括那些库期望出现在文件系统中的文件,通常在“/etc”中。我原以为 DNS 所需的所有 libc 都是 resolv .conf,但似乎并非如此......
  • 您正在使用 x86_64-unknown-linux-gnu 构建,它仍然需要链接 glibc 等。您是否尝试过使用 musl 目标构建以获得完全静态的二进制文件?
  • 不,但如果我理解正确,RUSTFLAGS="-C target-feature=+crt-static" 确实使我的应用程序完全静态。至少,在它上面运行 ldd 告诉我它不是动态链接的二进制文件,并且没有列出任何依赖项。

标签: docker rust


【解决方案1】:

我遇到了同样的问题。该问题可能是由于您的代码中的某些内容试图调用 getaddrinfo 系统函数,除非您的系统上存在一些其他共享库,否则该函数将不起作用。

对我来说,罪魁祸首是被 Reqwest 使用的 Hyper。 Hyper 默认使用getaddrinfo 解析DNS 名称。

有两种可能的解决方案:

  1. 将必要的库复制到您的映像中。 This SO answer 建议您需要复制的内容,但我没有尝试过。
  2. 确保您完全使用 rust 代码解析主机名。

对于第二个选项,您可以使用trust_dns_resolver 库作为getaddrinfo 的替代方案。如果您使用 Reqwest(和我一样),您可以通过启用 reqwest crate 上的 trust-dns 功能轻松完成此操作。

如果你直接使用 Hyper,看起来有点棘手。您需要创建Service<dns::Name> 的实现,以替代 Hyper 的默认 GaiResolver。首先,您可以查看(或复制)Reqwest 是如何做到的 here 更新: 看起来 hyper-trust-dns 板条箱是您在这种情况下想要的。

注意:如果您使用 TLS/HTTPS,您的程序在尝试使用 Openssl 时可能会遇到类似的问题。您可以类似地使用rustls 在“内部”移动 TLS 内容。对于 Reqwest,您可以使用 rustls-tls 功能标志和 default-features = false 来做到这一点。你也可以看看你是如何使用native-tls-vendored 来代替的。

【讨论】:

    猜你喜欢
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-05
    相关资源
    最近更新 更多