1. <em id="vzzs9"></em>
      <tbody id="vzzs9"></tbody>

    2. <span id="vzzs9"></span>
      <progress id="vzzs9"></progress>
      首頁 運維干貨4 個超實用的 Docker 鏡像構建技巧

      4 個超實用的 Docker 鏡像構建技巧

      運維派隸屬馬哥教育旗下專業運維社區,是國內成立最早的IT運維技術社區,歡迎關注公眾號:yunweipai
      領取學習更多免費Linux云計算、Python、Docker、K8s教程關注公眾號:馬哥linux運維

      最近做了一個好玩的工具,叫?xbin.io?。其中有一項工作是為不同的工具來構建 Docker 鏡像,讓他們都運行在 Docker 中(實際上,是兼容 Docker image 的其他 ?sandbox 系統,沒有直接用 Docker)。支持的工具越來越多,為了節省資源,Build 的 Docker image ?就越小越好,文件越少,其實啟動速度也會略微快一些,也會更安全一些。

      這篇文章來介紹一下做 Docker Image 的一些技巧。

      在之前的博客?Docker (容器) 的原理中介紹過 Docker image 是如何工作的。簡單來說,就是使用 Linux 的?overlayfs, overlay file system 可以做到,將兩個 file system merge ?在一起,下層的文件系統只讀,上層的文件系統可寫。如果你讀,找到上層就讀上層的,否則的話就找到下層的給你讀。然后寫的話會寫入到上層。這樣,其實對于最終用戶來說,可以認為只有一個 merge 之后的文件系統,用起來和普通文件系統沒有什么區別。

      有了這個功能,Docker 運行的時候,從最下層的文件系統開始,merge 兩層,得到新的 fs 然后再 merge 上一層,然后再 merge 最上一層,最后得到最終的 directory,然后用?chroot改變進程的 root 目錄,啟動 container。

      4 個超實用的 Docker 鏡像構建技巧插圖

      了解了原理之后,你會發現,這種設計對于 Docker 來說非常合適:

      1. 如果 2 個 image 都是基于 Ubuntu,那么兩個 Image 可以共用 Ubuntu 的 base image,只需要存儲一份;
      2. 如果 pull 新的 image,某一層如果已經存在,那么這一層之前的內容其實就不需要 pull 了;

      后面 build image 的技巧其實都是基于這兩點。

      另外稍微提一下,Docker image?其實就是一個 tar 包。一般來說我們通過?Dockerfile?用?docker built?命令來構建,但是其實也可以用其他工具構建,只要構建出來的?image 符合 Docker 的規范,就可以運行。比如,之前的博文?Build 一個最小的 Redis Docker Image就是用 Nix 構建出來的。

      技巧1:刪除緩存

      一般的包管理器,比如 aptpip 等,下載包的時候,都會下載緩存,下次安裝同一個包的時候不必從網絡上下載,直接使用緩存即可。

      但是在 Docker Image 中,我們是不需要這些緩存的。所以我們在?Dockerfile?中下載東西一般會使用這種命令:

      RUN dnf install -y --setopt=tsflags=nodocs \
          httpd vim && \
          systemctl enable httpd && \
          dnf clean all

      在包安裝好之后,去刪除緩存。

      一個常見的錯誤是,有人會這么寫:

      FROM fedora
      RUN dnf install -y mariadb
      RUN dnf install -y wordpress
      RUN dnf clean all

      Dockerfile 里面的每一個 RUN 都會創建一層新的 layer,如上所說,這樣其實是創建了 3 層  layer,前 2 層帶來了緩存,第三層刪除了緩存。如同 git 一樣,你在一個新的 commit 里面刪除了之前的文件,其實文件還是在 git 歷史中的,最終的 docker image 其實沒有減少。

      但是 Docker 有了一個新的功能,docker build --squash。squash 功能會在 Docker 完成構建之后,將所有的 layers 壓縮成一個 layer,也就是說,最終構建出來的 Docker image 只有一層。所以,如上在多個 RUN 中寫 clean 命令,其實也可以。我不太喜歡這種方式,因為前文提到的,多個 image 共享 base image 以及加速 pull 的 feature 其實就用不到了。

      一些常見的包管理器刪除緩存的方法:

      4 個超實用的 Docker 鏡像構建技巧插圖1

      另外,上面這個命令其實還有一個缺點。因為我們在同一個 RUN 中寫多行,不容易看出這個 dnf 到底安裝了什么。而且,第一行和最后一行不一樣,如果修改,diff 看到的會是兩行內容,很不友好,容易出錯。

      可以寫成這種形式,比較清晰。

      RUN true \
          && dnf install -y --setopt=tsflags=nodocs \
              httpd vim \
          && systemctl enable httpd \
          && dnf clean all \
          && true

      技巧2:改動不頻繁的內容往前放

      通過前文介紹過的原理,可以知道,對于一個 Docker image 有 ABCD 四層,B 修改了,那么 BCD 會改變。

      根據這個原理,我們在構建的時候可以將系統依賴往前寫,因為像 aptdnf 這些安裝的東西,是很少修改的。然后寫應用的庫依賴,比如 pip install,最后 copy 應用。

      比如下面這個 Dockerfile,就會在每次代碼改變的時候都重新 Build 大部分 layers,即使只改了一個網頁的標題。

      FROM python:3.7-buster
       
      # copy source
      RUN mkdir -p /opt/app
      COPY myapp /opt/app/myapp/
      WORKDIR /opt/app
       
      # install dependencies nginx
      RUN apt-get update && apt-get install nginx
      RUN pip install -r requirements.txt
      RUN chown -R www-data:www-data /opt/app
       
      # start server
      EXPOSE 8020
      STOPSIGNAL SIGTERM
      CMD ["/opt/app/start-server.sh"]

      我們可以改成,先安裝 Nginx,再單獨 copy?requirements.txt,然后安裝?pip?依賴,最后 copy 應用代碼。

      FROM python:3.7-buster
       
      # install dependencies nginx
      RUN apt-get update && apt-get install nginx
      COPY myapp/requirements.txt /opt/app/myapp/requirements.txt
      RUN pip install -r requirements.txt
       
      # copy source
      RUN mkdir -p /opt/app
      COPY myapp /opt/app/myapp/
      WORKDIR /opt/app
       
      RUN chown -R www-data:www-data /opt/app
       
      # start server
      EXPOSE 8020
      STOPSIGNAL SIGTERM
      CMD ["/opt/app/start-server.sh"]

      技巧3:構建和運行 Image 分離

      我們在編譯應用的時候需要很多構建工具,比如 gcc, golang 等。但是在運行的時候不需要。在構建完成之后,去刪除那些構建工具是很麻煩的。

      我們可以這樣:使用一個 Docker 作為 builder,安裝所有的構建依賴,進行構建,構建完成后,重新選擇一個 Base image,然后將構建的產物復制到新的 base image,這樣,最終的 image 只含有運行需要的東西。

      比如,這是安裝一個 golang 應用?pup?的代碼:

      FROM golang as build
      ENV CGO_ENABLED 0
      RUN go install github.com/ericchiang/pup@latest
       
      FROM alpine:3.15.4 as run
      COPY --from=build /go/bin/pup /usr/local/bin/pup

      我們使用 golang 這個 1G 多大的 image 來安裝,安裝完成之后將 binary 復制到 alpine, 最終的產物只有 10M 左右。這種方法特別適合一些靜態編譯的編程語言,比如 golang 和 rust.

      技巧4:檢查構建產物

      這是最有用的一個技巧了。

      dive 是一個 TUI,命令行的交互式 App,它可以讓你看到 docker 每一層里面都有什么。

      dive ubuntu:latest?命令可以看到 ubuntu image 里面都有什么文件。內容會顯示為兩側,左邊顯示每一層的信息,右邊顯示當前層(會包含之前的所有層)的文件內容,本層新添加的文件會用黃色來顯示。通過?tab?鍵可以切換左右的操作。

      4 個超實用的 Docker 鏡像構建技巧插圖2

      一個非常有用的功能是,按下 ctrl + U 可以只顯示當前層相比于前一層增加的內容,這樣,就可以看到增加的文件是否是預期的了。

      按 ctrl + Space 可以折疊起來所有的目錄,然后交互式地打開他們查看,就像是 Docker 中的 ncdu。

      參考資料:

      • https://jvns.ca/blog/2019/11/18/how-containers-work–overlayfs/
      • https://www.kernel.org/doc/html/latest/filesystems/overlayfs.html
      • http://docs.projectatomic.io/container-best-practices/

      鏈接:https://www.kawabangga.com/posts/4676

      (版權歸原作者所有,侵刪)

      本文鏈接:http://www.abandonstatusquo.com/42556.html

      網友評論comments

      發表評論

      您的電子郵箱地址不會被公開。

      暫無評論

      Copyright ? 2012-2022 YUNWEIPAI.COM - 運維派 京ICP備16064699號-6
      掃二維碼
      掃二維碼
      返回頂部
      久久久久亚洲国内精品|亚洲一区二区在线观看综合无码|欧洲一区无码精品色|97伊人久久超碰|一级a爱片国产亚洲精品