牛逼!下一代 Docker 鏡像構建神器

Docker經過讀取Dockerfile中的指令自動構建鏡像,Dockerfile是一個文本文件,其中依次包含構建給定鏡像所需的全部命令。html

上面的解釋摘自Docker的官方文檔並總結了Dockerfile的用途。Dockerfile的使用很是重要,由於它是咱們的藍圖,是咱們添加到Docker鏡像中的層的記錄。java

本文,咱們將學習如何利用BuildKit功能,這是Docker v18.09上引入的一組加強功能。集成BuildKit將爲咱們提供更好的性能,存儲管理和安全性。git

先決條件

  • Docker概念知識
  • 已安裝Docker(當前使用v19.03)
  • 一個Java應用程序(在本文中,我使用了一個Jenkins Maven示例應用程序)

讓咱們開始吧!github

簡單的Dockerfile示例

如下是一個包含Java應用程序的未優化Dockerfile的示例。咱們將逐步進行一些優化。docker

FROM debian
COPY . /app
RUN apt-get update
RUN apt-get -y install openjdk-11-jdk ssh emacs
CMD [「java」, 「-jar」, 「/app/target/my-app-1.0-SNAPSHOT.jar」]

在這裏,咱們可能會問本身:構建須要多長時間?爲了回答這個問題,讓咱們在本地開發環境上建立該Dockerfile,並讓Docker構建鏡像。npm

# enter your Java app folder
cd simple-java-maven-app-master
# create a Dockerfile
vim Dockerfile
# write content, save and exit
docker pull debian:latest # pull the source image
time docker build --no-cache -t docker-class . # overwrite previous layers
# notice the build time
0,21s user 0,23s system 0% cpu 1:55,17 total

此時,咱們的構建須要1m55s。json

若是咱們僅啓用BuildKit而沒有其餘更改,會有什麼不一樣嗎?vim

啓用BuildKit

BuildKit能夠經過兩種方法啓用:緩存

在調用Docker build命令時設置DOCKER_BUILDKIT = 1環境變量,例如:安全

time DOCKER_BUILDKIT=1 docker build --no-cache -t docker-class

將Docker BuildKit設置爲默認開啓,須要在/etc/docker/daemon.json進行以下設置,而後重啓:

{ "features": { "buildkit": true } }

BuildKit最初的效果

DOCKER_BUILDKIT=1 docker build --no-cache -t docker-class .
0,54s user 0,93s system 1% cpu 1:43,00 total

此時,咱們的構建須要1m43s。在相同的硬件上,構建花費的時間比之前少了約12秒。這意味着構建幾乎無需費力便可節約10%左右的時間。

如今讓咱們看看是否能夠採起一些額外的步驟來進一步改善。

從最小到最頻繁變化的順序

由於順序對於緩存很重要,因此咱們將COPY命令移到更靠近Dockerfile末尾的位置。

FROM debian
RUN apt-get update
RUN apt-get -y install openjdk-11-jdk ssh emacs
RUN COPY . /app
CMD [「java」, 「-jar」, 「/app/target/my-app-1.0-SNAPSHOT.jar」]

避免使用「COPY .」

選擇更具體的COPY參數,以免緩存中斷。僅複製所需內容。

FROM debian
RUN apt-get update
RUN apt-get -y install openjdk-11-jdk ssh vim
COPY target/my-app-1.0-SNAPSHOT.jar /app
CMD [「java」, 「-jar」, 「/app/my-app-1.0-SNAPSHOT.jar」]

apt-get update 和install命令一塊兒使用

這樣能夠防止使用過期的程序包緩存。

FROM debian
RUN apt-get update && \
    apt-get -y install openjdk-11-jdk ssh vim
COPY target/my-app-1.0-SNAPSHOT.jar /app
CMD [「java」, 「-jar」, 「/app/my-app-1.0-SNAPSHOT.jar」]

刪除沒必要要的依賴

在開始時,不要安裝調試和編輯工具,之後能夠在須要時安裝它們。

FROM debian
RUN apt-get update && \
    apt-get -y install --no-install-recommends \
    openjdk-11-jdk
COPY target/my-app-1.0-SNAPSHOT.jar /app
CMD [「java」, 「-jar」, 「/app/my-app-1.0-SNAPSHOT.jar」]

刪除程序包管理器緩存

你的鏡像不須要此緩存數據。藉此機會釋放一些空間。

FROM debian
RUN apt-get update && \
    apt-get -y install --no-install-recommends \
    openjdk-11-jdk && \
    rm -rf /var/lib/apt/lists/*
COPY target/my-app-1.0-SNAPSHOT.jar /app
CMD [「java」, 「-jar」, 「/app/my-app-1.0-SNAPSHOT.jar」]

儘量使用官方鏡像

使用官方鏡像有不少理由,例如減小鏡像維護時間和減少鏡像尺寸,以及預先配置鏡像以供容器使用。

FROM openjdk
COPY target/my-app-1.0-SNAPSHOT.jar /app
CMD [「java」, 「-jar」, 「/app/my-app-1.0-SNAPSHOT.jar」]

使用特定標籤

請勿使用latest標籤。

FROM openjdk:8
COPY target/my-app-1.0-SNAPSHOT.jar /app
CMD [「java」, 「-jar」, 「/app/my-app-1.0-SNAPSHOT.jar」]

尋找最小的鏡像

如下是openjdk鏡像列表。選擇最適合本身的最輕的那個鏡像。

REPOSITORY TAG標籤 SIZE大小
openjdk 8 634MB
openjdk 8-jre 443MB
openjdk 8-jre-slim 204MB
openjdk 8-jre-alpine 83MB

在一致的環境中從源構建

若是你不須要整個JDK,則可使用Maven Docker鏡像做爲構建基礎。

FROM maven:3.6-jdk-8-alpine
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn -e -B package
CMD [「java」, 「-jar」, 「/app/my-app-1.0-SNAPSHOT.jar」]

在單獨的步驟中獲取依賴項

能夠緩存–用於獲取依賴項的Dockerfile命令。緩存此步驟將加快構建速度。

FROM maven:3.6-jdk-8-alpine
WORKDIR /app
COPY pom.xml .
RUN mvn -e -B dependency:resolve
COPY src ./src
RUN mvn -e -B package
CMD [「java」, 「-jar」, 「/app/my-app-1.0-SNAPSHOT.jar」]

多階段構建:刪除構建依賴項

爲何要使用多階段構建?

  • 將構建與運行時環境分開
  • DRY方式

    • 具備開發,測試等環境的不一樣詳細信息
  • 線性化依賴關係
  • 具備特定於平臺的階段

    FROM maven:3.6-jdk-8-alpine AS builder
    WORKDIR /app
    COPY pom.xml .
    RUN mvn -e -B dependency:resolve
    COPY src ./src
    RUN mvn -e -B package
    
    FROM openjdk:8-jre-alpine
    COPY --from=builder /app/target/my-app-1.0-SNAPSHOT.jar /
    CMD [「java」, 「-jar」, 「/my-app-1.0-SNAPSHOT.jar」]

    若是你此時構建咱們的應用程序,

    time DOCKER_BUILDKIT=1 docker build --no-cache -t docker-class .
    0,41s user 0,54s system 2% cpu 35,656 total

    你會注意到咱們的應用程序構建須要大約35.66秒的時間。這是一個使人愉快的進步。

下面,咱們將介紹其餘場景的功能。

多階段構建:不一樣的鏡像風格

下面的Dockerfile顯示了基於Debian和基於Alpine的鏡像的不一樣階段。

FROM maven:3.6-jdk-8-alpine AS builder
…
FROM openjdk:8-jre-jessie AS release-jessie
COPY --from=builder /app/target/my-app-1.0-SNAPSHOT.jar /
CMD [「java」, 「-jar」, 「/my-app-1.0-SNAPSHOT.jar」]

FROM openjdk:8-jre-alpine AS release-alpine
COPY --from=builder /app/target/my-app-1.0-SNAPSHOT.jar /
CMD [「java」, 「-jar」, 「/my-app-1.0-SNAPSHOT.jar」]

要構建特定的鏡像,咱們可使用–target參數:

time docker build --no-cache --target release-jessie .

不一樣的鏡像風格(DRY /全局ARG)

ARG flavor=alpine
FROM maven:3.6-jdk-8-alpine AS builder
…
FROM openjdk:8-jre-$flavor AS release
COPY --from=builder /app/target/my-app-1.0-SNAPSHOT.jar /
CMD [「java」, 「-jar」, 「/my-app-1.0-SNAPSHOT.jar」]

ARG命令能夠指定要構建的鏡像。在上面的例子中,咱們指定alpine爲默認的鏡像,但咱們也能夠在docker build命令中,經過–build-arg flavor=參數指定鏡像。

time docker build --no-cache --target release --build-arg flavor=jessie .

併發

併發在構建Docker鏡像時很重要,由於它會充分利用可用的CPU線程。在線性Dockerfile中,全部階段均按順序執行。經過多階段構建,咱們可讓較小的依賴階段準備就緒,以供主階段使用它們。

BuildKit甚至帶來了另外一個性能上的好處。若是在之後的構建中不使用該階段,則在結束時將直接跳過這些階段,而不是對其進行處理和丟棄。

下面是一個示例Dockerfile,其中網站的資產是在一個assets階段中構建的:

FROM maven:3.6-jdk-8-alpine AS builder
…
FROM tiborvass/whalesay AS assets
RUN whalesay 「Hello DockerCon!」 > out/assets.html

FROM openjdk:8-jre-alpine AS release
COPY --from=builder /app/my-app-1.0-SNAPSHOT.jar /
COPY --from=assets /out /assets
CMD [「java」, 「-jar」, 「/my-app-1.0-SNAPSHOT.jar」]

這是另外一個Dockerfile,其中分別編譯了C和C ++庫,並在builder之後使用該階段。

FROM maven:3.6-jdk-8-alpine AS builder-base
…

FROM gcc:8-alpine AS builder-someClib
…
RUN git clone … ./configure --prefix=/out && make && make install

FROM g++:8-alpine AS builder-some CPPlib
…
RUN git clone … && cmake …

FROM builder-base AS builder
COPY --from=builder-someClib /out /
COPY --from=builder-someCpplib /out /

BuildKit應用程序緩存

BuildKit具備程序包管理器緩存的特殊功能。如下是一些緩存文件夾位置的示例:

包管理器路徑
apt /var/lib/apt/lists
go ~/.cache/go-build
go-modules $GOPATH/pkg/mod
npm ~/.npm
pip ~/.cache/pip

咱們能夠將此Dockerfile與上面介紹的在一致的環境中從源代碼構建中介紹的Dockerfile進行比較。這個較早的Dockerfile沒有特殊的緩存處理。咱們可使用–mount=type=cache來作到這一點。

FROM maven:3.6-jdk-8-alpine AS builder
WORKDIR /app
RUN --mount=target=. --mount=type=cache,target /root/.m2 \
    && mvn package -DoutputDirectory=/

FROM openjdk:8-jre-alpine
COPY --from=builder /app/target/my-app-1.0-SNAPSHOT.jar /
CMD [「java」, 「-jar」, 「/my-app-1.0-SNAPSHOT.jar」]

BuildKit的安全功能

BuildKit具備安全功能,下面的示例中,咱們使用了–mount=type=secret隱藏了一些機密文件,例如~/.aws/credentials。

FROM <baseimage>
RUN …
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials,required \
./fetch-assets-from-s3.sh
RUN ./build-scripts.sh

要構建此Dockerfile,須要使用–secret參數:

docker build --secret id=aws,src=~/.aws/credentials

還有爲了提升安全性,避免使用諸如COPY ./keys/private.pem /root .ssh/private.pem之類的命令,咱們可使用BuildKit中的ssh解決此問題:

FROM alpine
RUN apk add --no-cache openssh-client
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
ARG REPO_REF=19ba7bcd9976ef8a9bd086187df19ba7bcd997f2
RUN --mount=type=ssh,required git clone git@github.com:org/repo /work && cd /work && git checkout -b $REPO_REF

要構建此Dockerfile,你須要在ssh-agent中加載到你的SSH私鑰。

eval $(ssh-agent)
ssh-add ~/.ssh/id_rsa # this is the SSH key default location
docker build --ssh=default .

結論

本文,咱們介紹了使用Docker BuildKit優化Dockerfile,並所以加快了鏡像構建時間。這些速度的提升,能夠幫助咱們提升效率和節省計算能力。

來源:https://os.51cto.com/art/2021...

相關文章
相關標籤/搜索