Rust용 작은 Docker 이미지를 만드는 방법

Rust를 배포하기 위해 최소한의 Docker 이미지를 구축하면 많은 이점이 있습니다. 보안(공격 영역 감소)에 좋을 뿐만 아니라 배포 시간을 개선하고 비용을 절감하며(대역폭 및 스토리지 감소) 종속성 충돌의 위험을 줄일 수 있습니다.

목차


  • Code
  • FROM scratch (15.9MB)
  • FROM alpine (21.6MB)
  • FROM gcr.io/distroless/cc (33MB)
  • FROM buster-slim (79.4MB)
  • Conclusion
  • The code is on GitHub

  • 더 알고 싶으세요? Rust, Security 및 Cryptography와 공격적인 목적으로 Docker를 사용하는 경우에 대해 알아보려면 내 책Black Hat Rust을 살펴보십시오.

    암호



    "앱"은 다소 간단합니다. https://api.myip.com를 호출하고 결과를 인쇄하는 명령줄 유틸리티를 빌드할 것입니다.

    HTTPS 호출을 만드는 것은 일반적으로 TLS와 상호 작용하는 라이브러리가 필요하기 때문에 흥미롭습니다openssl. 그러나 가능한 가장 작은 Docker 이미지를 빌드하려면 프로그램을 정적으로 링크해야 하는데 정적으로 링크openssl하는 것은 그리 쉬운 일이 아닙니다. 그래서 openssl를 피하고 대신 rustls 을 사용합니다.

    잠시 동안 Jemalloc 항목을 무시합시다.

    $ cargo new myip
    


    Cargo.toml

    [package]
    name = "myip"
    version = "0.1.0"
    edition = "2018"
    
    # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
    
    [dependencies]
    serde = { version = "1", features = ["derive"] }
    reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls", "blocking"] }
    
    
    [target.'cfg(all(target_env = "musl", target_pointer_width = "64"))'.dependencies.jemallocator]
    version = "0.3"
    


    main.rs

    use serde::Deserialize;
    use std::error::Error;
    
    // Use Jemalloc only for musl-64 bits platforms
    #[cfg(all(target_env = "musl", target_pointer_width = "64"))]
    #[global_allocator]
    static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
    
    #[derive(Deserialize, Debug)]
    struct ApiRes {
        ip: String,
    }
    
    fn main() -> Result<(), Box<dyn Error>> {
        let res = reqwest::blocking::get("https://api.myip.com")?.json::<ApiRes>()?;
    
        println!("{}", res.ip);
    
        Ok(())
    }
    



    $ cargo run
         Running `target/debug/myip`
    127.0.0.1
    


    기스로부터



    크기: 15.9MB
    FROM scratch를 기본 이미지로 사용하려면 musl libcglibc에서 사용할 수 없기 때문에 프로그램을 scratch에 정적으로 연결해야 합니다. x86_64-unknown-linux-musl 대상을 사용하여 달성할 수 있습니다.

    이 접근 방식의 문제점은 musl 의 메모리 할당자가 속도에 대해 최적화되지 않고 특히 처리량이 많은 응용 프로그램을 처리할 때 가 될 수 있다는 것입니다.

    이것이 우리가 고도의 동시성 애플리케이션용으로 설계된 메모리 할당자 jemalloc 를 사용한 이유입니다.

    some people are reporting errors using this allocator에 유의하십시오. 따라서 로그를 주의하십시오 ;)

    데이터 포인트로 I've served millions of HTTP requests using it 문제가 없습니다.

    도커파일.스크래치

    ####################################################################################################
    ## Builder
    ####################################################################################################
    FROM rust:latest AS builder
    
    RUN rustup target add x86_64-unknown-linux-musl
    RUN apt update && apt install -y musl-tools musl-dev
    RUN update-ca-certificates
    
    # Create appuser
    ENV USER=myip
    ENV UID=10001
    
    RUN adduser \
        --disabled-password \
        --gecos "" \
        --home "/nonexistent" \
        --shell "/sbin/nologin" \
        --no-create-home \
        --uid "${UID}" \
        "${USER}"
    
    
    WORKDIR /myip
    
    COPY ./ .
    
    RUN cargo build --target x86_64-unknown-linux-musl --release
    
    ####################################################################################################
    ## Final image
    ####################################################################################################
    FROM scratch
    
    # Import from builder.
    COPY --from=builder /etc/passwd /etc/passwd
    COPY --from=builder /etc/group /etc/group
    
    WORKDIR /myip
    
    # Copy our build
    COPY --from=builder /myip/target/x86_64-unknown-linux-musl/release/myip ./
    
    # Use an unprivileged user.
    USER myip:myip
    
    CMD ["/myip/myip"]
    



    $ docker build -t myip:scratch -f Dockerfile.scratch .
    # ...
    $ docker run -ti --rm myip:scratch
    127.0.0.1
    


    고산에서



    크기: 21.6MB

    Alpine Linux은 musl libc 및 busybox를 기반으로 하는 보안 지향 경량 Linux 배포판입니다.
    FROM scratch 가 충분하지 않고 chromium 또는 ssh 와 같은 종속성을 설치하기 위해 패키지 관리자가 필요한 경우에 사용해야 합니다.
    musl libc 를 기반으로 하므로 FROM scratch 와 동일한 제약 조건이 있으며 x86_64-unknown-linux-musl 를 사용하여 Rust 프로그램을 정적으로 링크해야 합니다.

    Dockerfile.alpine

    ####################################################################################################
    ## Builder
    ####################################################################################################
    FROM rust:latest AS builder
    
    RUN rustup target add x86_64-unknown-linux-musl
    RUN apt update && apt install -y musl-tools musl-dev
    RUN update-ca-certificates
    
    # Create appuser
    ENV USER=myip
    ENV UID=10001
    
    RUN adduser \
        --disabled-password \
        --gecos "" \
        --home "/nonexistent" \
        --shell "/sbin/nologin" \
        --no-create-home \
        --uid "${UID}" \
        "${USER}"
    
    
    WORKDIR /myip
    
    COPY ./ .
    
    RUN cargo build --target x86_64-unknown-linux-musl --release
    
    ####################################################################################################
    ## Final image
    ####################################################################################################
    FROM alpine
    
    # Import from builder.
    COPY --from=builder /etc/passwd /etc/passwd
    COPY --from=builder /etc/group /etc/group
    
    WORKDIR /myip
    
    # Copy our build
    COPY --from=builder /myip/target/x86_64-unknown-linux-musl/release/myip ./
    
    # Use an unprivileged user.
    USER myip:myip
    
    CMD ["/myip/myip"]
    



    $ docker build -t myip:alpine -f Dockerfile.alpine .
    # ...
    $ docker run -ti --rm myip:alpine
    127.0.0.1
    


    distroless/cc에서



    크기: 38.3

    Google에서 관리하는 distroless 데비안 패키지를 사용하는 이미지 계열을 사용할 수도 있지만 최소한의 이미지를 만들기 위해 쓸모 없는 패키지를 모두 제거합니다. 따라서 더 이상 MUSL libc를 사용할 필요가 없습니다.

    여기서는 Rust가 풀기 위해 libgcc1가 필요하기 때문에 distroless/cc 이미지를 사용합니다.

    Dockerfile.distroless

    ####################################################################################################
    ## Builder
    ####################################################################################################
    FROM rust:latest AS builder
    
    RUN update-ca-certificates
    
    # Create appuser
    ENV USER=myip
    ENV UID=10001
    
    RUN adduser \
        --disabled-password \
        --gecos "" \
        --home "/nonexistent" \
        --shell "/sbin/nologin" \
        --no-create-home \
        --uid "${UID}" \
        "${USER}"
    
    
    WORKDIR /myip
    
    COPY ./ .
    
    # We no longer need to use the x86_64-unknown-linux-musl target
    RUN cargo build --release
    
    ####################################################################################################
    ## Final image
    ####################################################################################################
    FROM gcr.io/distroless/cc
    
    # Import from builder.
    COPY --from=builder /etc/passwd /etc/passwd
    COPY --from=builder /etc/group /etc/group
    
    WORKDIR /myip
    
    # Copy our build
    COPY --from=builder /myip/target/release/myip ./
    
    # Use an unprivileged user.
    USER myip:myip
    
    CMD ["/myip/myip"]
    



    $ docker build -t myip:distroless -f Dockerfile.distroless .
    # ...
    $ docker run -ti --rm myip:distroless
    127.0.0.1
    


    FROM 버스터 슬림



    크기: 79.4MB

    이 마지막 예에서는 debian:buster-slim를 기본 이미지로 사용합니다. Debian은 glibc를 기반으로 하므로 더 이상 x86_64-unknown-linux-musl 컴파일 대상을 사용할 필요가 없습니다.

    도커파일.데비안

    ####################################################################################################
    ## Builder
    ####################################################################################################
    FROM rust:latest AS builder
    
    RUN update-ca-certificates
    
    # Create appuser
    ENV USER=myip
    ENV UID=10001
    
    RUN adduser \
        --disabled-password \
        --gecos "" \
        --home "/nonexistent" \
        --shell "/sbin/nologin" \
        --no-create-home \
        --uid "${UID}" \
        "${USER}"
    
    
    WORKDIR /myip
    
    COPY ./ .
    
    # We no longer need to use the x86_64-unknown-linux-musl target
    RUN cargo build --release
    
    ####################################################################################################
    ## Final image
    ####################################################################################################
    FROM debian:buster-slim
    
    # Import from builder.
    COPY --from=builder /etc/passwd /etc/passwd
    COPY --from=builder /etc/group /etc/group
    
    WORKDIR /myip
    
    # Copy our build
    COPY --from=builder /myip/target/release/myip ./
    
    # Use an unprivileged user.
    USER myip:myip
    
    CMD ["/myip/myip"]
    



    $ docker build -t myip:debian -f Dockerfile.debian .
    # ...
    $ docker run -ti --rm myip:debian
    127.0.0.1
    


    결론




    $ docker images
    REPOSITORY    TAG           IMAGE ID       CREATED          SIZE
    myip     scratch       795604e74501   9 minutes ago    15.9MB
    myip     alpine        9a26400587a2   2 minutes ago    21.6MB
    myip     distroless    b789c964a680   19 seconds ago   33MB
    myip     debian        c388547b9486   12 seconds ago   79.4MB
    


    여기서는 Docker에 중점을 두었지만 이미지가 여전히 너무 크고 수행 중인 작업을 알고 있는 경우here are a few tricks to minimize Rust binary size 이미지 크기를 더 줄입니다.

    예를 들어 Cargo.toml에서 다음을 사용합니다.

    [profile.release]
    lto = true
    codegen-units = 1
    

    cargo build 단계 뒤에 Dockerfile에 다음을 추가합니다.

    RUN strip -s /myip/target/release/myip
    


    제공:

    $ docker images
    REPOSITORY        TAG           IMAGE ID       CREATED          SIZE
    myip              scratch       de26b0460262   17 minutes ago   4.2MB
    myip              alpine        4188ccc82662   6 minutes ago    9.81MB
    myip              distroless    c46e6a0c1ac3   7 seconds ago    25.6MB
    myip              debian        0eefb58278a8   4 seconds ago    72.8MB
    


    실제 Rust 경험, 보안 및 암호화에서 더 많은 것을 배우고 싶다면 무엇보다도 Docker를 사용하여 Rust 서비스를 만들고 배포하는 책을 썼습니다: Black Hat Rust .

    코드는 GitHub에 있습니다.



    평소와 같이 GitHub에서 코드를 찾을 수 있습니다: github.com/skerkour/kerkour.com (저장소에 별표를 표시하는 것을 잊지 마세요 🙏).

    좋은 웹페이지 즐겨찾기