映像檔構建最佳實踐
映像檔分層
透過使用 docker image history 指令,您可以查看用於建立映像檔中每一層的指令。
使用
docker image history指令來查看您所建立的getting-started映像檔中的分層。$ docker image history getting-started您應該會得到類似下方的輸出。
IMAGE CREATED CREATED BY SIZE COMMENT a78a40cbf866 18 seconds ago /bin/sh -c #(nop) CMD ["node" "src/index.j… 0B f1d1808565d6 19 seconds ago /bin/sh -c yarn install --production 85.4MB a2c054d14948 36 seconds ago /bin/sh -c #(nop) COPY dir:5dc710ad87c789593… 198kB 9577ae713121 37 seconds ago /bin/sh -c #(nop) WORKDIR /app 0B b95baba1cfdb 13 days ago /bin/sh -c #(nop) CMD ["node"] 0B <missing> 13 days ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B <missing> 13 days ago /bin/sh -c #(nop) COPY file:238737301d473041… 116B <missing> 13 days ago /bin/sh -c apk add --no-cache --virtual .bui… 5.35MB <missing> 13 days ago /bin/sh -c #(nop) ENV YARN_VERSION=1.21.1 0B <missing> 13 days ago /bin/sh -c addgroup -g 1000 node && addu… 74.3MB <missing> 13 days ago /bin/sh -c #(nop) ENV NODE_VERSION=12.14.1 0B <missing> 13 days ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 13 days ago /bin/sh -c #(nop) ADD file:e69d441d729412d24… 5.59MB每一行都代表映像檔中的一個層。此處的顯示方式將底層放在最下方,最新的層放在最上方。使用此功能,您還可以快速查看每一層的大小,有助於診斷龐大的映像檔。
您會注意到其中幾行被截斷了。如果您加上
--no-trunc旗標,就可以獲得完整的輸出。$ docker image history --no-trunc getting-started
層快取
現在您已經親眼見識了分層運作方式,有一個重要的觀念能幫助您縮短容器映像檔的構建時間。一旦某一層發生變更,所有後續的下游層都必須重新建立。
請查看您為入門應用程式(getting started app)建立的 Dockerfile。
# syntax=docker/dockerfile:1
FROM node:lts-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]回頭查看映像檔歷史輸出,您會發現 Dockerfile 中的每一條指令都會成為映像檔中的一個新層。您可能還記得,當您對映像檔進行變更時,yarn 相依套件必須重新安裝。每次構建都重新傳輸相同的相依套件其實沒有太大意義。
要解決這個問題,您需要重新建構您的 Dockerfile 以支援相依套件的快取。對於基於 Node 的應用程式,這些相依套件定義在 package.json 檔案中。您可以先只複製該檔案,安裝相依套件,然後再複製其餘檔案。這樣一來,只有當 package.json 發生變更時,才會重新建立 yarn 相依套件層。
更新 Dockerfile,先複製
package.json,安裝相依套件,然後再複製其餘檔案。# syntax=docker/dockerfile:1 FROM node:lts-alpine WORKDIR /app COPY package.json yarn.lock ./ RUN yarn install --production COPY . . CMD ["node", "src/index.js"]使用
docker build構建一個新的映像檔。$ docker build -t getting-started .您應該會看到類似下方的輸出。
[+] Building 16.1s (10/10) FINISHED => [internal] load build definition from Dockerfile => => transferring dockerfile: 175B => [internal] load .dockerignore => => transferring context: 2B => [internal] load metadata for docker.io/library/node:lts-alpine => [internal] load build context => => transferring context: 53.37MB => [1/5] FROM docker.io/library/node:lts-alpine => CACHED [2/5] WORKDIR /app => [3/5] COPY package.json yarn.lock ./ => [4/5] RUN yarn install --production => [5/5] COPY . . => exporting to image => => exporting layers => => writing image sha256:d6f819013566c54c50124ed94d5e66c452325327217f4f04399b45f94e37d25 => => naming to docker.io/library/getting-started現在,對
src/static/index.html檔案進行修改。例如,將<title>修改為 "The Awesome Todo App"。現在再次使用
docker build -t getting-started .構建 Docker 映像檔。這一次,您的輸出看起來會有點不同。[+] Building 1.2s (10/10) FINISHED => [internal] load build definition from Dockerfile => => transferring dockerfile: 37B => [internal] load .dockerignore => => transferring context: 2B => [internal] load metadata for docker.io/library/node:lts-alpine => [internal] load build context => => transferring context: 450.43kB => [1/5] FROM docker.io/library/node:lts-alpine => CACHED [2/5] WORKDIR /app => CACHED [3/5] COPY package.json yarn.lock ./ => CACHED [4/5] RUN yarn install --production => [5/5] COPY . . => exporting to image => => exporting layers => => writing image sha256:91790c87bcb096a83c2bd4eb512bc8b134c757cda0bdee4038187f98148e2eda => => naming to docker.io/library/getting-started首先,您應該會發現構建速度快多了。而且,您會看到有幾個步驟使用了先前快取的層。推送和拉取此映像檔及其更新的速度也將會快得多。
多階段建置
多階段構建(Multi-stage builds)是一個非常強大的工具,有助於利用多個階段來建立映像檔。它們有幾個優點:
- 將構建時的相依套件與執行時的相依套件分開
- 透過僅交付應用程式執行所需的檔案來減少整體映像檔大小
Maven/Tomcat 範例
在構建 Java 應用程式時,您需要 JDK 來將原始碼編譯為 Java 位元組碼。但是,生產環境中並不需要該 JDK。此外,您可能會使用 Maven 或 Gradle 等工具來協助構建應用程式,這些工具在最終映像檔中也不需要。多階段構建可以解決此問題。
# syntax=docker/dockerfile:1
FROM maven AS build
WORKDIR /app
COPY . .
RUN mvn package
FROM tomcat
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps 在此範例中,您使用一個階段(稱為 build)來執行使用 Maven 的實際 Java 構建。在第二個階段(從 FROM tomcat 開始),您從 build 階段複製檔案。最終映像檔僅為最後建立的階段,可以使用 --target 旗標覆蓋。
React 範例
構建 React 應用程式時,您需要 Node 環境來將 JS 程式碼(通常為 JSX)、SASS 樣式表等編譯為靜態 HTML、JS 和 CSS。如果您不進行伺服器端渲染,您的生產環境構建甚至不需要 Node 環境。您可以將靜態資源部署在靜態 nginx 容器中。
# syntax=docker/dockerfile:1
FROM node:lts AS build
WORKDIR /app
COPY package* yarn.lock ./
RUN yarn install
COPY public ./public
COPY src ./src
RUN yarn run build
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html在先前的 Dockerfile 範例中,它使用了 node:lts 映像檔來執行構建(最大化層快取),然後將輸出複製到 nginx 容器中。
總結
在本節中,您學習了一些映像檔構建的最佳實踐,包括層快取和多階段構建。
相關資訊
下一步
在下一節中,您將了解可以用來繼續深入學習容器的其他資源。
接下來做什麼