第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Dockerfile多階段構建鏡像

之前學習部署 Docker 應用時,我們搭建過一個 redis 服務,然后編寫并運行了一個統(tǒng)計訪問次數(shù)的 flask 應用。

現(xiàn)在,我們使用 Dockerfile,將這個 flask 應用也制作成鏡像,此外,在這個鏡像中,可以包含一個 helloworld 二進制程序,這個 helloworld 的源碼就是我們學習 rootfs 時用到的 helloworld.c。

1. 實戰(zhàn):直接構建鏡像

首先 我們需要新建一個目錄 dockerfiledir,用于存放 Dockerfile 文件。

mkdir dockerfiledir
# 在這個目錄下新建個空文件 Dockerfile,之后填充內容 
touch dockerfiledir/Dockerfile

新建一個目錄code,用來存放flask和c的源代碼。

mkdir code

將之前 app.py 和 helloworld.c 兩個源碼文件放入到 code 目錄下,當前的目錄結構應該是這樣的:

圖片描述

進入 dockerfiledir 目錄,編輯 Dockerfile 文件:

# 從 ubuntu系統(tǒng)鏡像開始構建
FROM ubuntu	
# 標記鏡像維護者信息
MAINTAINER user <user@imooc.com>
# 切換到鏡像的/app目錄,不存在則新建此目錄
WORKDIR /app
# 將 宿主機的文件拷貝到容器中
COPY ../code/app.py .
COPY ../code/helloworld.c .
# 安裝依賴 編譯helloworld
RUN apt update >/dev/null 2>&1  && \
    apt install -y gcc python3-flask python3-redis >/dev/null 2>&1 && \
    cc /app/helloworld.c -o /usr/bin/helloworld
# 設定執(zhí)行用戶為user
RUN useradd user
USER user
# 設定flask所需的環(huán)境變量
ENV FLASK_APP app
# 默認啟動執(zhí)行的命令
CMD ["flask", "run", "-h", "0.0.0.0"]
# 將flask的默認端口暴露出來
EXPOSE 5000

然后執(zhí)行:

docker build .

出現(xiàn)如下報錯:

COPY failed: Forbidden path outside the build context: ../code/app.py ()

解決這個問題,需要引入一個重要的概念——構建上下文。

docker build .命令在執(zhí)行時,當前目錄.被指定成了構建上下文,此目錄中的所有文件或目錄都將被發(fā)送到 Docker 引擎中去,Dockerfile中的切換目錄和復制文件等操作只會對上下文中的內容生效。

Tips:在默認情況下,如果不額外指定 Dockerfile 的話,會將構建上下文對應的目錄下 Dockerfile 的文件作為 Dockerfile。但這只是默認行為,實際上 Dockerfile 的文件名并不要求必須為 Dockerfile,而且并不要求必須位于上下文目錄中,比如可以用 -f ../demo.txt參數(shù)指定父級目錄的demo.txt文件作為 Dockerfile。

一般來說,我們習慣使用默認的文件名 Dockerfile,將其置于鏡像構建上下文目錄.中。

我們需要將 code 目錄納入到上下文中,一個直接的方法是,調整dockerfile中的COPY指令的路徑。

# 將 .. 改為 .
COPY ./code/app.py .
COPY ./code/helloworld.c .

然后將 code 所在的目錄指定為構建上下文。由于我們當前的目錄是 dockerfiledir,所以我們執(zhí)行:

docker build -f ./Dockerfile ..

如果你留意查看構建過程,會發(fā)現(xiàn)類似這樣的提示:

Sending build context to Docker daemon 421.309 MB

如果..目錄除了code和dockerfiledir,還包含其他的文件或目錄,docker build也會將這個數(shù)據(jù)傳輸給Docker,這會增加構建時間。
避免這種情況,有兩種解決方法:

  • 使用.dockerignore文件:在構建上下文的目錄下新建一個.dockerignore文件來指定在傳遞給 docker 時需要忽略掉的文件或文件夾。.dockerignore 文件的排除模式語法和 Git 的 .gitignore 文件相似。

  • 使用一個干凈的目錄作為構建上下文(推薦):使用 Dockerfile 構建鏡像時最好是將 Dockerfile 放置在一個新建的空目錄下。然后將構建鏡像所需要的文件添加到該目錄中。

在我們當前的示例中,將code目錄移入dockerfiledir。

mv ../code .

現(xiàn)在的目錄層級如下:

執(zhí)行 docker build -t myhello . 執(zhí)行構建即可獲得我們的自定義鏡像 myhello。

使用鏡像 myhello 創(chuàng)建 myhello 容器:

# 這里使用--net=host,方便使用之前章節(jié)中部署的redis容器服務,與之進行數(shù)據(jù)交換
docker run -dit --net=host  --name myhello myhello 

確保部署之前的 redis 容器正常啟動,然后在 Docker 宿主機的瀏覽器中訪問http://127.0.0.1:5000
圖片描述

說明 myhello 中的 flask 應用已經(jīng)正常運行了。接下來,我們再運行測試一下編譯的 helloworld。

docker exec myhello /usr/bin/helloworld

得到輸出:

Hello, World!

Tips: myhello容器已經(jīng)完成任務,記得執(zhí)行docker rm -f myhello刪除它.

2. 改進: 使用多階段構建

在鏡像構建過程中,我們的 helloworld.c 源碼以及相關編譯工具和依賴也被構建到了鏡像中,這導致我們最終得到的鏡像偏大。

理想狀態(tài)應該是使用了一個系統(tǒng)鏡像生成的容器,編譯源碼后再將編譯的程序導入到最終的鏡像中,這樣就會縮減體積,并且將不同目的的操作有效分離開,但是按照我們之前掌握的知識,這樣實現(xiàn)需要兩個Dockerfile 文件。

使用多階段構建,我們可以在一個 Dockerfile 中使用多個 FROM 語句。每個 FROM 指令都可以使用不同的鏡像,并表示開始一個新的構建階段。很方便的將一個階段的文件復制到另外一個階段,在最終的鏡像中保留下需要的內容即可。

我們還是在 Dockerfile 文件的同一目錄,新建一個新的構建腳本,命名為 Dockerfile-multi-stage 便于區(qū)分:

#從ubuntu鏡像開始構建, 將第一階段命名為`build`,在其他階段需要引用的時候使用`--from=build`參數(shù)即可。
FROM ubuntu AS build
# 將宿主機的源碼拷貝到鏡像中
COPY ./code/helloworld.c .
# 安裝依賴 并編譯源碼
RUN apt update >/dev/null 2>&1 && \
    apt install -y gcc >/dev/null 2>&1 && \
    cc helloworld.c -o /usr/bin/helloworld

# 第二階段 從官方的python:alpine基礎鏡像開始構建
FROM python:alpine
# 鏡像維護者信息
MAINTAINER user <user@imooc.com>
# 將第一階段構建的helloworld 導入到此鏡像中
COPY --from=build /usr/bin/helloworld /usr/bin/helloworld
# 安裝flask 和 redis 的依賴
RUN pip install flask redis >/dev/null 2>&1 
# 設定鏡像在切換到/app目錄路徑
WORKDIR /app
# 將源碼導入到鏡像
COPY ./code/app.py .
# 設定執(zhí)行用戶為user
RUN useradd user
USER user
# 設定flask所需的環(huán)境變量
ENV FLASK_APP app
# 默認啟動執(zhí)行的命令
CMD ["flask", "run", "-h", "0.0.0.0"]
# 將flask的默認端口暴露出來
EXPOSE 5000

執(zhí)行 build 命令:

docker build -f Dockerfile-multi-stage -t myhello-multi-stage .

使用此鏡像運行一個容器:

# 這里使用--net=host,方便使用之前章節(jié)中部署的redis容器服務,與之進行數(shù)據(jù)交換
docker run -dit --net=host --name myhello-multi-stage myhello-multi-stage

自行測試一下這個容器吧。

3. 小結

通過以上內容,相信大家對 Dockerfile 的使用又有了新的認知,我們在構建鏡像的時候,一定要有合理的規(guī)劃, 在自己不熟悉的基礎鏡像上定義鏡像的時候,不妨先用它運行一個容器,在容器中過一遍流程, 弄清最終的鏡像中到底應該包含哪些內容,再來調整構建腳本。

這里有一些 Dockerfile 的一般規(guī)范:

  1. 通過 Dockerfile 構建的鏡像所啟動的容器越快越好,這樣可以快速啟停增刪容器服務(下面幾條也是為第1條服務的);
  2. 避免安裝不必要的包,必要時使用多階段構建;
  3. 一個容器盡量只專注做一件事情;
  4. 最小化鏡像層數(shù), 將重復功能的 RUN、COPY、ADD 等指令縮減合并, 但一定要保證 Dockerfile 可讀性。

當然,這些建議僅供參考,不要拘泥于它,要根據(jù)自己的使用場景來做權衡。