docker鏡像分層
2024-01-21 00:46 youxin 閱讀(122) 評論(0) 收藏 舉報鏡像分層
您知道可以查看鏡像是如何組成的嗎?使用docker image history命令,您可以查看鏡像中的每個層次的創建命令。
1.使用docker image history命令查看您在教程中創建的getting-started鏡像中的層次。
docker image history getting-started
您應該會得到類似以下的輸出(日期/ID可能不同)。
IMAGE CREATED CREATED BY SIZE COMMENT
05bd8640b718 53 minutes ago CMD ["node" "src/index.js"] 0B buildkit.dockerfile.v0
<missing> 53 minutes ago RUN /bin/sh -c yarn install --production # b… 83.3MB buildkit.dockerfile.v0
<missing> 53 minutes ago COPY . . # buildkit 4.59MB buildkit.dockerfile.v0
<missing> 55 minutes ago WORKDIR /app 0B buildkit.dockerfile.v0
<missing> 10 days ago /bin/sh -c #(nop) CMD ["node"] 0B
<missing> 10 days ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B
<missing> 10 days ago /bin/sh -c #(nop) COPY file:4d192565a7220e13… 388B
<missing> 10 days ago /bin/sh -c apk add --no-cache --virtual .bui… 7.85MB
<missing> 10 days ago /bin/sh -c #(nop) ENV YARN_VERSION=1.22.19 0B
<missing> 10 days ago /bin/sh -c addgroup -g 1000 node && addu… 152MB
<missing> 10 days ago /bin/sh -c #(nop) ENV NODE_VERSION=18.12.1 0B
<missing> 11 days ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 11 days ago /bin/sh -c #(nop) ADD file:57d621536158358b1… 5.29MB
每一行代表鏡像中的一個層次。這里的顯示從底部顯示基礎層次,最新層次在頂部。使用這個命令,您還可以快速查看每個層次的大小,有助于診斷大型鏡像。
2.您會注意到其中一些行被截斷了。如果添加--no-trunc標志,您將獲得完整的輸出(是的...有趣的是您使用截斷標志來獲得未截斷的輸出,不是嗎?)
docker image history --no-trunc getting-started
層緩存
Layer Caching
現在你已經看到了層疊的作用,有一個重要的教訓可以幫助減少容器映像的構建時間。
一旦一個層發生變化,所有下游的層也必須重新創建
讓我們再次查看我們使用的 Dockerfile...
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
回到映像歷史輸出,我們可以看到 Dockerfile 中的每個命令都會成為映像中的一個新層。你可能還記得,當我們對映像進行更改時,必須重新安裝 yarn 依賴項。有沒有辦法解決這個問題呢?每次構建時都攜帶相同的依賴關系似乎沒有太多意義,對嗎?
為了解決這個問題,我們需要重新構建我們的 Dockerfile,以支持依賴項的緩存。對于基于 Node 的應用程序,這些依賴項在 package.json 文件中定義。那么,如果我們首先只復制該文件,安裝依賴項,然后再復制其他所有內容呢?然后,只有在 package.json 發生更改時,我們才重新創建 yarn 依賴項。有道理吧?
To fix this, we need to restructure our Dockerfile to help support the caching of the dependencies. For Node-based applications, those dependencies are defined in the package.json file. So, what if we copied only that file in first, install the dependencies, and then copy in everything else? Then, we only recreate the yarn dependencies if there was a change to the package.json. Make sense?
1.更新 Dockerfile 以首先復制 package.json,然后安裝依賴項,最后復制其他所有內容。
FROM node:18-alpine
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --production
COPY . .
CMD ["node", "src/index.js"]
2.在與 Dockerfile 相同的文件夾中創建一個名為 .dockerignore 的文件,并包含以下內容。
node_modules
.dockerignore 文件是一種選擇性復制僅與映像相關的文件的簡便方法。你可以在這里閱讀更多關于這個的內容。在這種情況下,應該在第二個 COPY 步驟中省略 node_modules 文件夾,因為否則它可能會覆蓋由 RUN 步驟中的命令創建的文件。有關為 Node.js 應用程序建議使用這種方法以及其他最佳實踐的更多詳細信息,請參閱它們有關 Docker 化 Node.js web 應用程序的指南。
3.使用 docker build 構建一個新的映像。
docker build -t getting-started .
你應該會看到如下輸出...
[+] Building 16.1s (10/10) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 175B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/node:18-alpine 0.0s
=> [internal] load build context 0.8s
=> => transferring context: 53.37MB 0.8s
=> [1/5] FROM docker.io/library/node:18-alpine 0.0s
=> CACHED [2/5] WORKDIR /app 0.0s
=> [3/5] COPY package.json yarn.lock ./ 0.2s
=> [4/5] RUN yarn install --production 14.0s
=> [5/5] COPY . . 0.5s
=> exporting to image 0.6s
=> => exporting layers 0.6s
=> => writing image sha256:d6f819013566c54c50124ed94d5e66c452325327217f4f04399b45f94e37d25 0.0s
=> => naming to docker.io/library/getting-started 0.0s
你會看到所有的層都被重新構建了。這很好,因為我們對 Dockerfile 進行了相當大的更改。
4.現在,對 src/static/index.html 文件進行更改(比如將 <title> 更改為 "The Awesome Todo App")。
5.再次使用 docker build -t getting-started . 構建 Docker 映像。這次,你的輸出應該有所不同。
[+] Building 1.2s (10/10) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 37B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/node:18-alpine 0.0s
=> [internal] load build context 0.2s
=> => transferring context: 450.43kB 0.2s
=> [1/5] FROM docker.io/library/node:18-alpine 0.0s
=> CACHED [2/5] WORKDIR /app 0.0s
=> CACHED [3/5] COPY package.json yarn.lock ./ 0.0s
=> CACHED [4/5] RUN yarn install --production 0.0s
=> [5/5] COPY . . 0.5s
=> exporting to image 0.3s
=> => exporting layers 0.3s
=> => writing image sha256:91790c87bcb096a83c2bd4eb512bc8b134c757cda0bdee4038187f98148e2eda 0.0s
=> => naming to docker.io/library/getting-started 0.0s
首先,你應該注意到構建速度大大加快了!你會看到有幾個步驟正在使用先前緩存的層。所以,太好了!我們正在使用構建緩存。推送和拉取這個映像以及對它的更新也將快得多。太好了!
多階段構建
Multi-Stage Builds
雖然在本教程中我們不會深入討論它,但多階段構建是一個非常強大的工具,它通過使用多個階段來創建鏡像來幫助我們。它們提供了幾個優點,包括:
- 將構建時的依賴項與運行時的依賴項分開
- 通過僅運送應用程序運行所需的內容來減小鏡像的總大小
- Separate build-time dependencies from runtime dependencies
- Reduce overall image size by shipping only what your app needs to run
Maven/Tomcat示例
在構建基于Java的應用程序時,需要JDK來將源代碼編譯為Java字節碼。但是,生產環境中不需要該JDK。您可能還在使用諸如Maven或Gradle等工具來幫助構建應用程序。這些工具在我們的最終鏡像中也是不需要的。多階段構建有所幫助。
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開始),我們從構建階段復制文件。最終鏡像只是最后一個階段被創建的鏡像(可以使用--target標志進行覆蓋)。
React示例
在構建React應用程序時,我們需要一個Node環境來編譯JS代碼(通常是JSX)、SASS樣式表等,以生成靜態HTML、JS和CSS。盡管如果我們不執行服務器端渲染,甚至在生產構建中都不需要Node環境。為什么不將靜態資源運送到一個靜態的nginx容器中呢?
FROM node:18 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
在這里,我們使用node:18鏡像來執行構建(最大化層緩存),然后將輸出復制到一個nginx容器中。很酷,對吧?
總結
通過了解鏡像的結構,我們可以更快地構建鏡像并減少更改的數量。掃描鏡像使我們有信心運行和分發的容器是安全的。多階段構建還幫助我們減小鏡像的總大小,并通過將構建時的依賴項與運行時的依賴項分開來增加最終容器的安全性。
下一步做什么?
雖然我們已經完成了我們的研討會,但關于容器還有很多要學習的內容!我們不會在這里深入探討,但以下是一些可以接下來研究的領域!
容器編排
在生產環境中運行容器是有挑戰的。你不希望只是登錄到一臺機器上運行docker run或docker compose up。為什么呢?如果容器停止運行會發生什么?如何在多臺機器上進行擴展?容器編排解決了這個問題。像Kubernetes、Swarm、Nomad和ECS等工具都以略有不同的方式來解決這個問題。
一般的想法是你有“管理器”接收預期狀態。這個狀態可能是“我想運行兩個實例的我的Web應用程序并暴露端口80。”然后管理器會查看集群中的所有機器,并將工作委派給“工作”節點。管理器會監視變化(比如容器退出),然后努力使實際狀態反映預期狀態。
Cloud Native Computing Foundation項目
CNCF是各種開源項目的供應商中立的家園,包括Kubernetes、Prometheus、Envoy、Linkerd、NATS等等!你可以在這里查看已畢業和孵化的項目,以及整個CNCF景觀。有很多項目可以幫助解決監控、日志記錄、安全性、鏡像注冊、消息傳遞等問題!
所以,如果你是新手容器領域和云原生應用開發,歡迎!請加入社區,提問,并繼續學習!我們很高興有你的加入!
https://zhuanlan.zhihu.com/p/667867199
浙公網安備 33010602011771號