教學課程:在 VS Code 中使用磁碟區將資料保存在容器應用程式中

在本教學課程中,您將了解如何將資料保存在容器應用程式中。 當您執行或更新該應用程式時,資料仍然可用。 有兩種主要類型的磁碟區用來保存資料。 本教學課程專注於具名磁碟區

您也會了解「繫結掛接」,其會控制主機上的確切掛接點。 您可以使用繫結掛接來保存資料,但其也可以將更多資料新增至容器。 在處理應用程式時,您可以使用繫結掛接,將原始程式碼掛接至容器,讓其看到程式碼變更、回應,以及讓您立即看到變更。

本教學課程也會介紹映像分層、圖層快取和多階段建置。

在本教學課程中,您會了解如何:

  • 了解跨容器的資料。
  • 使用具名磁碟區保存資料。
  • 使用繫結裝載。
  • 檢視映像圖層。
  • 快取相依性。
  • 了解多階段建置。

必要條件

本教學課程延續上一個教學課程:使用 Visual Studio Code 建立和共用 Docker 應用程式。 從這一項開始,其中包含必要條件。

了解跨容器的資料

在本節中,您將啟動兩個容器,並在每個容器中建立一個檔案。 在一個容器中建立的檔案無法在另一個容器中使用。

  1. 使用下列命令啟動 ubuntu 容器:

    docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
    

    此命令會使用 && 來叫用兩個命令。 第一個部分會挑選單一亂數,並將其寫入 /data.txt。 第二個命令正在監看檔案,讓容器保持執行中。

  2. 在 VS Code 的 Docker 區域中,以滑鼠右鍵按一下 ubuntu 容器,然後選取 [連結殼層]

    Screenshot shows the Docker extension with a container selected and a context menu with Attach Shell selected.

    終端機隨即開啟,其正在執行 Ubuntu 容器中的殼層。

  3. 執行下列命令以查看 /data.txt 檔案的內容。

    cat /data.txt
    

    終端機會顯示介於 1 與 10000 之間的數字。

    若要使用命令列來查看此結果,請使用 docker ps 命令取得容器識別碼,然後執行下列命令。

    docker exec <container-id> cat /data.txt
    
  4. 呼叫另一個 ubuntu 容器。

    docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
    
  5. 使用此命令來查看資料夾內容。

    docker run -it ubuntu ls /
    

    那裡應該沒有 data.txt 檔案,因為其只寫入第一個容器的臨時空間。

  6. 選取這兩個 Ubuntu 容器。 按一下滑鼠右鍵並選取 [移除]。 從命令列中,您可以使用 docker rm -f 命令將其移除。

使用具名磁碟區保存您的待辦事項資料

根據預設,待辦事項應用程式會將其資料儲存在 SQLite 資料庫 (位於 /etc/todos/todo.db) 中。 SQLite 資料庫是將資料儲存在單一檔案中的關聯式資料庫。 此方法適用於小型專案。

您可以在主機上保存單一檔案。 當您使其可供下一個容器使用時,應用程式可以挑選其離開的位置。 藉由建立磁碟區並將其連結或「掛接」至其中儲存資料的資料夾,您可以保存資料。 容器會寫入 todo.db 檔案,且該資料會保存至磁碟區中的主機。

針對本節,請使用具名磁碟區。 Docker 會維護磁碟上磁碟區的實體位置。 請參閱磁碟區的名稱,而 Docker 會提供正確的資料。

  1. 使用 docker volume create 命令建立磁碟區。

    docker volume create todo-db
    
  2. 在 [容器] 底下,選取 [入門],然後按一下滑鼠右鍵。 選取 [停止] 以停止應用程式容器。

    若要從命令列停止容器,請使用 docker stop 命令。

  3. 使用下列命令啟動 [入門] 容器。

    docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
    

    volume 參數會指定要掛接的磁碟區,以及位置 /etc/todos

  4. 重新整理瀏覽器以重新載入應用程式。 如果您已關閉瀏覽器視窗,請移至 http://localhost:3000/。 將一些項目新增至您的待辦事項清單。

    Screenshot shows the sample app with several items added to the list.

  5. 移除待辦事項應用程式的 [入門] 容器。 以滑鼠右鍵按一下 Docker 區域中的容器,然後選取 [移除] 或使用 docker stopdocker rm 命令。

  6. 使用相同的命令啟動新的容器:

    docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
    

    此命令會掛接與之前相同的磁碟機。 重新整理您的瀏覽器。 您新增的項目仍在清單中。

  7. 再次移除 [入門] 容器。

下面討論的具名磁碟區和繫結掛接是預設 Docker 引擎安裝所支援的主要類型磁碟區。

屬性 具名磁碟區 繫結掛接
主機位置 Docker 選擇 您的控制項
掛接範例 (使用 -v) my-volume:/usr/local/data /path/to/data:/usr/local/data
將容器內容填入新的磁碟區 No
支援磁碟區驅動程式 No

有許多磁碟區驅動程式外掛程式可用來支援 NFS、SFTP、NetApp 等等。 這些外掛程式對於在叢集環境中的多個主機上執行容器特別重要,例如 Swarm 或 Kubernetes。

如果您想知道 Docker「實際」儲存資料的位置,請執行下列命令。

docker volume inspect todo-db

查看輸出,類似於下列結果。

[
    {
        "CreatedAt": "2019-09-26T02:18:36Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
        "Name": "todo-db",
        "Options": {},
        "Scope": "local"
    }
]

Mountpoint 是儲存資料的實際位置。 在大部分的電腦上,您需要根存取權,才能從主機存取此目錄。

使用繫結裝載

透過「繫結掛接」,您可以控制主機上的確切掛接點。 此方法會保存資料,但通常用來將更多資料提供給容器。 您可以使用繫結掛接,將原始程式碼掛接至容器,讓其看到程式碼變更、回應,以及讓您立即看到變更。

若要執行容器來支援開發工作流程,您將採取下列步驟:

  1. 移除任何 getting-started 容器。

  2. app 資料夾中,執行下列命令。

    docker run -dp 3000:3000 -w /app -v ${PWD}:/app node:20-alpine sh -c "yarn install && yarn run dev"
    

    此命令包含下列參數。

    • -dp 3000:3000 與之前相同。 以中斷連結模式執行,並建立連接埠對應。
    • -w /app 容器內的工作目錄。
    • -v ${PWD}:/app" 將目前目錄從容器中的主機繫結掛接至 /app 目錄。
    • node:20-alpine 要使用的映像。 此映像是應用程式的基礎映像,來自 Dockerfile
    • sh -c "yarn install && yarn run dev" 一個命令。 其會使用 sh 啟動殼層,並執行 yarn install 以安裝所有相依性。 然後其會執行 yarn run dev。 如果您查看 package.jsondev 指令碼正在啟動 nodemon
  3. 您可以使用 docker logs 來監看記錄。

    docker logs -f <container-id>
    
    $ nodemon src/index.js
    [nodemon] 2.0.20
    [nodemon] to restart at any time, enter `rs`
    [nodemon] watching path(s): *.*
    [nodemon] watching extensions: js,mjs,json
    [nodemon] starting `node src/index.js`
    Using sqlite database at /etc/todos/todo.db
    Listening on port 3000
    

    當您看到此清單上的最後一個項目時,應用程式正在執行。

    當您完成監看記錄時,請選取終端機視窗中的任何金鑰,或在外部視窗中選取 Ctrl+C

  4. 在 VS Code 中,開啟 src/static/js/app.js。 變更第 109 行上 [新增項目] 按鈕的文字。

    - {submitting ? 'Adding...' : 'Add Item'}
    + {submitting ? 'Adding...' : 'Add'}
    

    儲存您的變更。

  5. 重新整理您的瀏覽器。 您應該會看到變更。

    Screenshot shows the sample app with the new text on the button.

檢視映像圖層

您可以查看組成映像的圖層。 執行 docker image history 命令以查看用來在映像內建立每個圖層的命令。

  1. 使用 docker image history 查看您稍早在教學課程中所建立「入門」映像中的圖層。

    docker image history getting-started
    

    您的結果應該類似此輸出。

    IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
    a78a40cbf866        18 seconds ago      /bin/sh -c #(nop)  CMD ["node" "/app/src/ind…   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   
    

    每一行都代表映像中的一個圖層。 輸出顯示基礎圖層位於底部,而最新圖層位於頂端。 使用這項資訊,您可以看到每個圖層的大小,這可協助診斷大型映像。

  2. 有數行遭到截斷。 如果新增 --no-trunc 參數,您將取得完整輸出。

    docker image history --no-trunc getting-started
    

快取相依性

一旦圖層變更,所有下游圖層也都必須重新建立。 以下再次為 Dockerfile

FROM node:20-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "/app/src/index.js"]

Dockerfile 中的每個命令都會成為映像中的新圖層。 若要將圖層數目降至最低,您可以重新建構 Dockerfile 以支援相依性的快取。 針對節點型應用程式,這些相依性是在 package.json 檔案中定義的。

方法是首先只複製該檔案、安裝相依性,然後複製其他所有項目。 如果 package.json 發生了變更,此程序只會重新建立 yarn 相依性。

  1. 更新 Dockerfile 以先在 package.json 中進行複製、安裝相依性,然後複製其他所有項目。 以下是新的檔案:

    FROM node:20-alpine
    WORKDIR /app
    COPY package.json yarn.lock ./
    RUN yarn install --production
    COPY . .
    CMD ["node", "/app/src/index.js"]
    
  2. 使用 docker build 建置新的映像。

    docker build -t getting-started .
    

    您應該會看到類似下列結果的輸出:

    Sending build context to Docker daemon  219.1kB
    Step 1/6 : FROM node:12-alpine
    ---> b0dc3a5e5e9e
    Step 2/6 : WORKDIR /app
    ---> Using cache
    ---> 9577ae713121
    Step 3/6 : COPY package* yarn.lock ./
    ---> bd5306f49fc8
    Step 4/6 : RUN yarn install --production
    ---> Running in d53a06c9e4c2
    yarn install v1.17.3
    [1/4] Resolving packages...
    [2/4] Fetching packages...
    info fsevents@1.2.9: The platform "linux" is incompatible with this module.
    info "fsevents@1.2.9" is an optional dependency and failed compatibility check. Excluding it from installation.
    [3/4] Linking dependencies...
    [4/4] Building fresh packages...
    Done in 10.89s.
    Removing intermediate container d53a06c9e4c2
    ---> 4e68fbc2d704
    Step 5/6 : COPY . .
    ---> a239a11f68d8
    Step 6/6 : CMD ["node", "/app/src/index.js"]
    ---> Running in 49999f68df8f
    Removing intermediate container 49999f68df8f
    ---> e709c03bc597
    Successfully built e709c03bc597
    Successfully tagged getting-started:latest
    

    已重建所有圖層。 這是預期的結果,因為您已變更 Dockerfile

  3. src/static/index.html 進行變更。 例如,變更標題以顯示「The Awesome Todo App」。

  4. 現在再次使用 docker build 建置 Docker 映像。 這次,您的輸出看起來應該有點不同。

    Sending build context to Docker daemon  219.1kB
    Step 1/6 : FROM node:12-alpine
    ---> b0dc3a5e5e9e
    Step 2/6 : WORKDIR /app
    ---> Using cache
    ---> 9577ae713121
    Step 3/6 : COPY package* yarn.lock ./
    ---> Using cache
    ---> bd5306f49fc8
    Step 4/6 : RUN yarn install --production
    ---> Using cache
    ---> 4e68fbc2d704
    Step 5/6 : COPY . .
    ---> cccde25a3d9a
    Step 6/6 : CMD ["node", "/app/src/index.js"]
    ---> Running in 2be75662c150
    Removing intermediate container 2be75662c150
    ---> 458e5c6f080c
    Successfully built 458e5c6f080c
    Successfully tagged getting-started:latest
    

    因為您正在使用建置快取,所以其應該會快得多。

多階段建置

多階段建置是一個極其強大的工具,可協助使用多個階段來建立映像。 其有數個優點:

  • 將建置階段相依性與執行階段相依性分開
  • 僅透過傳送應用程式需要執行的內容,來降低整體映像大小

本節提供簡短的範例。

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」開始,第二個階段會在來自 build 階段的檔案中進行複製。 最終映像只是要建立的最後一個階段,可以使用 --target 參數將其覆寫。

React 範例

建置 React 應用程式時,您需要 Node 環境,才能將 JavaScript 程式碼、SASS 樣式表單等編譯成靜態 HTML、JavaScript 和 CSS。 如果您未執行伺服器端轉譯,則甚至不需要 Node 環境,即可進行生產建置。

FROM node:20-alpine 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:20 映像來執行建置,這會最大化圖層快取,然後將輸出複製到 nginx 容器。

清除資源

請保留到目前為止您所做的一切,以繼續這一系列的教學課程。

下一步

您已了解為容器應用程式保存資料的選項。

接下來您想要做什麼?