容器化應用程式:從零開始

理解了容器基礎後,下一步是學習如何將自己的應用程式容器化。這是Docker最核心的應用場景,也是開發者必須掌握的技能。

容器化流程概述

容器化應用程式的一般流程包括:

  1. 分析應用程式依賴
  2. 建立Dockerfile
  3. 構建映像檔
  4. 測試容器
  5. 發布映像檔

範例應用介紹

我們以一個簡單的Node.js網頁應用為例:

// app.js
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.send('Hello from containerized app!');
});

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`);
});

Docker 容器與 AI 整合實戰:從基礎到微服務佈署

容器技術概覽與實作路徑

Docker 容器技術徹底改變了應用程式的開發與佈署方式,讓我們能夠在不同環境中保持一致的執行結果。本文將帶領你深入瞭解 Docker 的核心概念、佈署流程以及與 AI 整合的實務應用。

為何容器技術如此重要?

在容器出現前,開發者經常面臨「在我的電腦上可以執行,但在生產環境卻失敗」的困境。主要原因是開發環境與生產環境間的差異—不同版本的函式庫依性。Docker 透過創造一種簡單的方式解決此問題:將應用程式與其所有相依性封裝在一起,確保佈署到生產環境的應用程式與開發測試的完全一致。

容器技術的標準單位是映像檔(image),標準執行環境則是容器(container)。映像檔包含應用程式執行所需的一切資源。簡而言之,Docker 解決了應用程式在生產環境無法運作的問題,並使應用程式的封裝和執行變得輕鬆簡單。

容器技術的全域觀點

容器是目前最流行的應用程式封裝和執行方式。相較於虛擬機器,容器更小、更快、更具可攜性,並且適用於以現有程式語言編寫的現有應用程式—無需學習新語言或框架!

將應用程式執行為容器主要有兩個步驟:

  1. 將應用程式封裝為映像檔
  2. 以容器形式執行

將應用程式封裝為映像檔的過程稱為「容器化」(containerization)。這個過程會取得應用程式原始碼,包含所有相依性,並將其封裝成映像檔。這個映像檔通常很小,但包含執行應用所需的一切。

一旦擁有映像檔,就可以使用 Docker 等工具將其作為容器執行。從底層來看,容器是一個隔離的執行環境—一個專用於單一應用程式的作業系統隔離部分。每個容器是作業系統的隔離部分,每個容器執行單一應用程式,彼此間互不幹擾。

我們可以將映像檔視為停止的容器,將容器視為執行中的映像檔。容器化應用程式的主要步驟為:

  1. 開發應用程式
  2. 將應用程式及其相依性封裝為映像檔
  3. 將映像檔上載至映像檔儲存函式庫選)
  4. 以容器形式執行

整個過程非常簡單,玄貓將在本文中多次展示這個流程。

映像檔的深入理解

從高層次來看,映像檔是由多個層(layers)組成的集合,這些層共同構成了應用程式及其相依性。

映像檔通常包含三個主要層:底層是最小化的作業系統和檔案系統,中間層是應用程式本身,頂層是相依性。當這些層堆積積疊在一起時,我們稱之為映像檔,它們包含了以容器形式執行應用程式所需的一切。

建立映像檔最常見的方式是使用 Dockerfile 和 docker build 命令。Dockerfile 是一份指令清單,告訴 docker build 如何構建映像檔。例如:

FROM golang:1.20-alpine    # 起始基礎作業系統
COPY go.mod go.sum .       # 複製應用程式和相依性檔案
RUN go mod download        # 安裝相依性
ENTRYPOINT [ "/bin/server" ] # 如何啟動應用程式

構建過程從 Dockerfile 頂部開始,依次執行每條指令:

  1. 下載基礎作業系統
  2. 複製應用程式和相依性清單
  3. 安裝相依性
  4. 設定執行應用程式的命令

「容器映像檔」、「Docker 映像檔」和「OCI 映像檔」這些術語指的是同一件事物。通常,我們簡稱它們為「映像檔」。

容器的運作方式

你可以從單一映像檔啟動一個或多個容器。如果你是開發人員,可以將映像檔和容器視為類別似於類別和物件的關係—我們以類別似於從類別建立物件的方式從映像檔建立容器。如果你是系統管理員或 DevOps 工作者,可以將它們視為類別似於 VM 範本和執行中的 VM—我們以類別似於從 VM 範本建立 VM 的方式從映像檔建立容器。

例如,以下命令從名為 my-image 的映像檔啟動一個名為 web 的新容器:

$ docker run --name web my-image

映像檔儲存函式庫egistry)

容器映像檔儲存函式庫儲和檢索映像檔的集中場所,有時稱為容器映像檔儲存函式庫ocker 映像檔儲存函式庫OCI 映像檔儲存函式庫 儲存和檢索映像檔的動作稱為推播(push)和提取(pull)。例如,將映像檔推播到映像檔儲存函式庫從映像檔儲存函式庫映像檔。

以下命令展示了將映像檔推播到 GitHub Container Registry:

$ docker push ghcr.io/nigelpoulton/gsd-book:web
c41833b44d91: Pushed
170db43947cc: Pushed
<省略部分輸出>
a7d0e2584121: Pushed

下一個命令展示了從 Docker Hub 提取映像檔:

$ docker pull docker.io/nigelpoulton/gsd-book:web
web: Pulling from nigelpoulton/gsd-book
63b65145d645: Pull complete
<省略部分輸出>
55d7c29dff3f: Pull complete
Digest: sha256:3b497b2cc153ee8c4ccc...1c8c1b72a27efd
docker.io/nigelpoulton/gsd-book:web

大多數映像檔儲存函式庫額外功能,如存取控制、漏洞掃描和與自動化建構管道的整合。

開放容器計劃(OCI)

開放容器計劃(Open Container Initiative, OCI)是負責開發和維護核心標準的治理機構,這些標準使容器生態系統能夠蓬勃發展。

目前它維護三個規範:

  • 映像檔規範:定義映像檔格式的標準,如結構、內容和元資料
  • 執行時規範:定義如何解壓映像檔並將其作為容器執行
  • 分發規範:標準化透過映像檔儲存函式庫容器映像檔的方式

我們經常將這些標準比作鐵軌的標準化。例如,擁有標準軌距(4英尺8.5英寸,1,435毫米)的鐵軌推動了鐵路的擴張,因為火車製造商有信心他們的火車可以在任何使用標準軌距的軌道上執行。OCI標準同樣推動了容器生態系統的擴張和繁榮。

以下術語分別指代相同事物:

  • 映像檔:容器映像檔、Docker映像檔、OCI映像檔
  • 容器:Docker容器、Linux容器、OCI容器
  • **映像檔儲存函式庫:容器映像檔儲存函式庫ocker映像檔儲存函式庫CI映像檔儲存函式庫#### 容器與虛擬機器的比較

容器和虛擬機器都是流行的應用程式封裝方式,也都是虛擬化的形式:

在封裝方面,容器比虛擬機器(VM)更小、更快、更具可攜性。 在虛擬化方面,VM虛擬化硬體,而容器虛擬化作業系統(OS)。例如:

  • 每個VM都看起來、聞起來、感覺起來像一個實體伺服器
  • 每個容器都看起來、聞起來、感覺起來像一個常規作業系統

容器模型的一個主要優勢是分享作業系統。例如,要執行兩個應用程式的VM模式,你需要三個作業系統:分享主機上的OS,加上兩個VM各自的OS。每個OS都是完整的系統,消耗CPU、記憶體和磁碟空間。相比之下,容器模型只需要分享主機的OS。

這意味著任何硬體都能執行比VM多得多的容器。也常見在虛擬機器上執行容器,這種設定通常每個VM執行多個容器。

總結來說,容器虛擬化作業系統,比VM更小、更快、更具可攜性。這意味著任何基礎設施都能執行比VM更多的容器。

微服務架構

在容器出現之前,我們構建單體應用程式(Monolithic Application)。這是指將所有功能作為單一複雜應用程式開發、佈署和管理的應用程式。

單體應用程式面臨以下挑戰:

  • 開發緩慢,因為所有功能都在同一個程式碼函式庫- 佈署風險高,因為更新任何功能都需要重新佈署整個應用程式
  • 擴充套件效率低,因為必須擴充套件整個應用程式,而非單個功能
  • 技術債累積快,因為更改一個部分可能影響其他部分

微服務架構則將應用程式分解為獨立的服務,每個服務專注於特定功能並可獨立佈署。這種方法帶來許多好處:更快的開發、更安全的佈署、更有效的擴充套件,以及更好的故障隔離。

容器技術的出現使微服務架構變得更加可行,因為容器提供了輕量級、隔離的環境,非常適合執行單一服務。容器的標準化也簡化了不同服務間的協作和整合。

容器與AI的結合

容器技術已成為佈署AI應用的標準方式。將AI模型和應用程式容器化有許多優勢:

  1. 一致的環境:確保AI模型在開發和生產環境中有相同的依賴性和設定
  2. 可重複佈署:輕鬆在不同環境中複製相同的AI設定
  3. 資源隔離:確保AI工作負載不會與其他應用程式競爭資源
  4. 快速擴充套件:根據需求輕鬆擴充套件AI服務
  5. 版本控制:簡化AI模型的版本管理和回復

在實際應用中,玄貓發現容器化AI應用能夠顯著提高開發速度和佈署可靠性。特別是在處理大語言模型(LLM)時,容器化可以大幅簡化複雜的相依性管理。

本章內容概覽

本章將帶領讀者從Docker基礎知識開始,逐步掌握容器化應用的完整流程:

  1. 學習重要概念和專業術語
  2. 安裝Docker環境
  3. 佈署和管理第一個容器
  4. 容器化簡單的Web應用
  5. 瞭解容器映像檔和Docker Hub
  6. 佈署和管理多容器微服務應用
  7. 使用Docker佈署本地AI聊天機器人,與聊天機器人互動,並檢查其設定

在我多年的容器技術實踐中,發現Docker確實改變了應用開發和佈署的遊戲規則。從最初的單體應用轉向微服務架構,再到現在的AI應用整合,容器技術一直是推動這些轉變的核心力量。本文將帶你一步掌握這項關鍵技術,為你的技術工具箱增添強大的新能力。

微服務架構:從困境到轉機

單體應用的困境

單體應用雖然在早期開發中佔有一席之地,但隨著應用規模擴大,它們的缺點逐漸凸顯:

  • 複雜度高與開發困難:隨著功能增加,程式碼函式庫龐大與難以管理
  • 更新與修補風險大:即使只更改小部分功能,也需要重新佈署整個應用
  • 擴充套件效率低:無法針對特定功能進行精確的資源擴充套件

以圖1.9中的單體應用為例,如果你需要修補報表功能,必須將整個應用程式停機、更新整套系統,然後小心翼地重新啟動所有功能。這種情況下,如果報表功能獨立於主應用程式之外,你就能夠獨立更新它而不影響其他部分。

同樣的,當年底報表查詢量激增時,單體應用無法只針對報表功能進行擴充套件。唯一的選擇就是將整個應用程式遷移到更強大的伺服器或虛擬機器上,這顯然既浪費資源又缺乏靈活性。

微服務:現代應用程式的解決方案

微服務是一種現代設計模式,它將應用程式的每個功能都作為獨立的小型應用程式來開發、佈署和管理。微服務(Microservice)這個名稱本身就揭示了它的本質:

  • 微小的(Micro)
  • 應用程式(Service)

如果將圖1.9中的單體應用轉換為微服務架構,意味著六個不同功能將被開發、佈署和管理為六個小型獨立應用程式。圖1.10展示了這種重新設計的微服務應用程式架構,每個微服務都構建為自己的映像檔並佈署為獨立容器。每個服務可以由專屬的小型開發團隊負責,與與其他微服務透過網路進行鬆散耦合。最重要的是,每個微服務可以獨立更新和擴充套件,擴充套件方式則是透過增加或移除容器來實作。

儘管架構上有這些差異,但使用者與應用程式的互動方式和獲得的輸出結果完全相同。從使用者角度看,應用程式沒有任何不同。

簡而言之,微服務是一種設計模式,將個別應用功能開發為小型獨立應用程式,以容器形式執行,透過網路通訊,並為使用者提供統一的應用體驗。這使得獨立修補、更新和擴充套件個別應用元件成為可能。

Linux容器與Windows容器

Docker同時支援Linux和Windows容器。

如圖1.11所示,Linux容器在Linux主機上執行Linux應用程式,而Windows容器則在Windows主機上執行Windows應用程式。

這個規則的例外是安裝了WSL 2的Windows主機。WSL 2(Windows Subsystem for Linux)為Windows主機提供了Linux核心,這意味著像Windows 11專業版這樣的主機可以同時執行Windows和Linux容器。

儘管如此,絕大多數容器仍然是Linux容器,Windows容器相對罕見。

容器與AI

越來越多的組織和開發者轉向Docker和容器作為構建、測試和佈署本地AI模型的首選方式。這意味著容器將在AI應用程式的未來中扮演越來越重要的角色。

在後續章節中,你將使用Docker在本地電腦上佈署一個先進的AI聊天機器人,它使用流行的Ollama AI執行時和你選擇的大語言模型(LLM)。佈署完成後,你將與聊天機器人協作完成一個簡單的編碼任務。

專業術語回顧

以下是本章中出現的主要術語:

  • OCI:開放容器倡議(Open Container Initiative)。這是一個輕量級治理機構,為底層容器技術(如映像檔、執行時和登入檔)建立和維護標準。Docker建立符合OCI的映像檔,實作符合OCI的執行時,而Docker Hub則是符合OCI的登入檔。

  • 映像檔(Image):包含單一應用程式、所有必需依賴項以及將應用程式作為容器啟動所需的元資料。有時我們稱它們為OCI映像檔、容器映像檔或Docker映像檔。

  • 容器(Container):容器是作業系統的一個隔離部分,設計用於執行單一應用程式。對應用程式來說,容器看起來與常規作業系統相同。容器比虛擬機器更小、更快、更具可攜性。

  • 登入檔(Registry):集中儲存和檢索映像檔的地方。我們有時稱它們為OCI登入檔,而儲存和檢索映像檔的過程稱為推播和提取。我們也有用於儲存和提取AI模型的登入檔,但這些通常與OCI登入檔分開。

  • 主機(Host):每個容器都在主機上執行。主機可以是實體伺服器或虛擬機器,可以是Linux或Windows,並執行Docker等容器執行時。主機提供所有容器分享的作業系統。例如,同一主機上的10個容器都分享主機的作業系統核心。

  • 核心(Kernel):核心是作業系統的核心功能。我們有時交替使用核心和作業系統這兩個術語。

  • 微服務(Microservices):微服務是現代應用程式的一種設計模式,每個功能都作為自己的小型應用程式(微服務)開發、佈署和管理。每個微服務佈署為容器,實作頻繁更新和精確擴充套件。

  • Linux容器:Linux容器執行Linux應用程式,必須在具有Linux核心的主機上執行。這可以是執行WSL 2後端的Windows主機。幾乎所有容器都是Linux容器。

  • Windows容器:Windows容器執行Windows應用程式,必須在Windows主機上執行。Windows容器非常罕見。

  • WSL 2:WSL是Windows Subsystem for Linux的縮寫,允許Windows機器執行Linux容器。

  • Ollama:Ollama是一個開放原始碼平台,用於在本地機器上執行LLMs(大語言模型AI系統)。它與Docker配合良好,可在Docker容器內執行。

  • 模型(Model):AI程式的行話。例如,我們通常說在Docker容器中執行模型,而不是說在Docker容器中執行AI。

在本章中,你瞭解到容器是執行現代應用程式最流行的方式。你還瞭解到容器比虛擬機器更小、更快、更具可攜性。

容器化是將應用程式及其所有依賴項封裝成容器映像檔的過程。我們通常將映像檔儲存在登入檔中以便於存取。

執行容器涉及從登入檔中提取映像檔並從映像檔啟動容器。你可以從單個映像檔建立多個容器。

微服務是一種將複雜應用程式開發、佈署和管理為一組較小應用程式的方法。這些小型應用程式被稱為微服務,它們透過網路通訊形成一個有用的應用程式,可以輕鬆更新和擴充套件。

Linux和Windows應用程式都可以容器化。Linux應用程式需要具有Linux核心的主機,而Windows應用程式需要具有Windows核心的主機。幾乎所有容器都是Linux容器。

最後,你瞭解到容器是執行本地AI模型的絕佳平台,並有望在AI應用程式的未來中發揮重要作用。

取得Docker環境

有許多工具可以讓你構建和執行容器。然而,作為入門書籍,我們將專注於兩個最簡單的工具:

  • Docker Desktop
  • Multipass

我們還將示範如何取得Docker帳戶(它們是免費的)以及如何安裝Git命令列工具以下載範例應用程式。

Docker Desktop

Docker Desktop是使用Docker的最佳方式。它安裝簡單,擁有絕佳的使用者介面,並提供完整的Docker體驗。

如果你安裝Docker Desktop,就可以毫不費力地跟隨每個範例操作。

圖2.1展示了Docker Desktop的容器標籤頁。它顯示了一個容器,並提供了啟動、停止、刪除等簡單按鈕。左側有用於映像檔管理、磁碟區管理、漏洞管理(Docker Scout)和處理構建的標籤頁。還有一個蓬勃發展的擴充套件市場。

關於Docker Desktop授權的簡要說明:

個人使用Docker Desktop是免費的。然而,如果你的公司有超過250名員工或年收入超過1000萬美元,你必須付費才能將其用於工作。

安裝和測試Docker Desktop

安裝Docker Desktop非常簡單:

  1. 在網上搜尋Docker Desktop
  2. 下載適合你係統的安裝程式
  3. 啟動安裝程式並按照指示按下一步、下一步、下一步

Windows使用者在提示時應該安裝WSL 2子系統。

安裝完成後,你可能需要手動啟動應用程式。Mac使用者執行時在頂部的選單列中會看到一個鯨魚圖示,而Windows使用者則在底部的系統託盤中看到它。點選鯨魚可以顯示一些基本控制元件,並讓你開啟圖形介面。它還顯示Docker Desktop是否正在執行。

Windows使用者可能會有一個在Linux容器和Windows容器之間切換的額外選項。你需要切換到Linux容器才能跟隨範例操作。如果你沒有看到這個選項,你已經在Linux容器模式下執行,可以開始使用了。

開啟命令列並輸入一些命令,看Docker是否已安裝並正常工作。

$ docker --version
Docker version 27.4.0-rc3, build 9ea09fd
$ docker compose version
Docker Compose version v2.31.0-desktop.2

恭喜,Docker Desktop已安裝,你現在擁有一個完整的Docker開發環境,可以用來跟隨範例操作。

Multipass

如果你已經安裝了Docker Desktop,就不需要Multipass。

Multipass是一個適用於Linux、Mac和Windows的免費工具,它使建立執行Docker的虛擬機器變得容易。它不如Docker Desktop易用,也沒有所有相同的功能。例如,你不會獲得精美的圖形介面或漏洞掃描。

安裝和測試Multipass

存取https://multipass.run/install並安裝適合你係統的版本。

安裝完成後,開啟命令列並執行以下命令來建立一個名為gsd-vm的新虛擬機器(“gsd"是getting started with docker的縮寫)。該命令使用docker範本建立一個已安裝Docker的虛擬機器。

$ multipass launch docker --name gsd-vm

下載範本和啟動虛擬機器需要一兩分鐘。

列出虛擬機器以確保它正確啟動。

$ multipass ls
Name    State    IPv4             Image
gsd-vm  Running  192.168.64.37    Ubuntu 24.04 LTS
                 172.17.0.1
                 172.18.0.1

記下虛擬機器的192.168.x.x IP位址,因為你在後面的範例中會需要它。

執行multipass shell gsd-vm命令連線到虛擬機器,然後執行一些docker命令來驗證安裝。

你可以隨時輸入exit結束虛擬機器並回傳本地機器。同樣,你可以輸入multipass shell gsd-vm回傳虛擬機器。

Docker Hub和Docker帳戶

本章後面的一些範例將推播映像檔到Docker Hub。其他登入檔也存在,你可以使用它們。然而,如果你還沒有其他登入檔的帳戶,玄貓建議你從Docker Hub開始,因為它使用簡單,並且長期穩定。

存取hub.docker.com並點選Sign up按鈕。這將帶你到註冊頁面,在那裡你可以註冊一個免費帳戶。

一旦你有了帳戶,你可以用Docker Desktop登入。這也會無縫登入Docker Hub。如果你不使用Docker Desktop,從安裝了Docker的主機或虛擬機器執行docker login命令。

安裝Git命令列工具

範例應用程式的源程式碼和設定檔案託管在GitHub上。取得它們的最簡單方法是使用git命令列工具克隆它們。

別擔心如果你不知道如何使用Git,沒人真正完全掌握它 ;-)

在網上搜尋"install git cli"並按照你係統的說明進行操作。

安裝完成後,執行以下命令。它將建立一個g

容器世界的探索與實踐

Docker Desktop 與替代方案

Docker Desktop 是在個人電腦上使用 Docker 最簡便的方式,它提供了流暢的使用者介面和豐富的功能特性。對於個人專案和學習用途,Docker Desktop 是完全免費的。然而,若您的公司員作業員數超過 250 人或年收入超過 1000 萬美元,則需要購買商業授權才能將其用於工作環境。

對於尋求替代方案的開發者,Multipass 是個不錯的選擇。它能夠啟動一個輕量級虛擬機器,其中已預先安裝 Docker 引擎。雖然 Multipass 不具備 Docker Desktop 的所有功能,但足以應付本文中的範例操作。

值得一提的是,Docker 個人帳號是免費的,它讓您能夠存取 Docker Hub。當然,市場上還有許多其他工具可用於建立和管理容器。

執行容器的基本操作

讓我們透過實際操作來熟悉容器的基本生命週期管理,包括建立、停止、重啟和刪除容器。這個簡單的練習不會花太多時間,但能幫助您強化對前面章節所學知識的理解。

前置準備

您需要一個 Docker 環境來跟隨操作。如果您正在使用 Docker Desktop,請開啟一個新的命令列視窗。若您使用的是 Multipass,請執行以下命令登入您的 Docker 虛擬機器(假設您的虛擬機器名稱為 gsd-vm):

$ multipass shell gsd-vm

執行一個容器

在開始操作前,讓我們確認系統中沒有預先存在的容器或映像檔。

檢查是否有執行中的容器:

$ docker ps
CONTAINER ID   IMAGE   COMMAND   STATUS   PORTS   NAMES

檢查本地是否有映像檔:

$ docker images
REPOSITORY   TAG   IMAGE ID   CREATED   SIZE

現在讓我們來執行第一個容器:

$ docker run -d --name test -p 5555:8080 nigelpoulton/gsd-book

執行這個命令後,您會看到 Docker 在本地找不到指定的映像檔,因此從 Docker Hub 提取了它。接著 Docker 下載了所有映像層,最後顯示了映像的摘要、成功訊息以及新建容器的 ID。

讓我們來解析這個命令:

  • docker run 是啟動新容器的命令
  • -d 引數讓容器在背景執行,不會佔用您的終端
  • --name test 為容器命名為 “test”
  • -p 5555:8080 將主機的 5555 連線埠對映到容器內的 8080 連線埠
  • nigelpoulton/gsd-book 是容器根據的映像檔名稱

這個容器執行了一個監聽 8080 連線埠的網頁伺服器。透過對映到主機的 5555 連線埠,我們可以使用瀏覽器存取容器中的應用程式。

測試容器

如果您使用 Docker Desktop,請在瀏覽器中開啟 http://localhost:5555/

如果您使用 Multipass,請在瀏覽器中開啟 Multipass 虛擬機器的 IP 地址加上 5555 連線埠。您可以透過在虛擬機器中執行 ip a | grep 192 或在外部執行 multipass list 命令來找到 IP 地址。

應用程式應該會正常執行!您剛使用 docker run 啟動了一個新容器,它從 Docker Hub 提取映像檔並將應用程式解封裝到新容器中,同時將應用程式暴露在您的本地機器上。

檢視容器和映像檔

再次執行 docker ps 檢視執行中的容器:

$ docker ps
CTR ID   IMAGE                  ...   PORTS                  NAMES
f89..710 nigelpoulton/gsd-book  ...   0.0.0.0:5555->8080    test

檢視本地映像檔:

$ docker images
REPOSITORY              TAG     IMAGE ID      CREATED      SIZE
nigelpoulton/gsd-book   latest  262...236     5 min ago    111MB

現在您的系統上有了 nigelpoulton/gsd-book 映像檔的本地副本。

停止、重啟和刪除容器

停止容器:

$ docker stop test
test

此時如果您重新整理瀏覽器頁面,應用程式將無法載入,因為容器已停止。

重啟容器:

$ docker start test
test

應用程式現在應該又能正常執行了。

刪除容器:

$ docker rm test -f
test

容器已被刪除,但您仍然保留了映像檔的本地副本。

使用 Docker Desktop 管理容器

Docker Desktop 提供了圖形化介面來管理容器。以下是使用 Docker Desktop 進行相同操作的步驟:

  1. 點選系統選單或系統託盤中的 Docker 鯨魚圖示,選擇「前往儀錶板」選項,開啟 Docker Desktop 的「容器」檢視。

  2. 切換到「映像檔」檢視,您應該能看到之前提取的 nigelpoulton/gsd-book:latest 映像檔。點選映像檔右側「操作」欄中的播放/執行三角形按鈕,這將開啟「執行新容器」對話方塊。

  3. 展開「可選設定」並填寫表單:

    • 容器名稱:test
    • 連線埠對映:主機連線埠 5555,容器連線埠 8080
  4. 點選藍色的「執行」按鈕,然後切換回「容器」標籤頁檢視執行中的容器。

  5. 點選容器名稱可以檢視詳細資訊,包括日誌、設定、檔案系統瀏覽等。

  6. 從「容器」頁面,您可以點選「操作」欄下的停止方塊來停止容器,或點選垃圾桶圖示來刪除容器。

  7. 切換到「映像檔」檢視,同樣點選「操作」欄下的垃圾桶圖示並確認刪除,即可刪除映像檔。

Docker Desktop 還提供了許多其他功能,包括映像檔漏洞掃描、層級檢視等高階特性。

容器化應用程式

接下來,玄貓將帶您體驗如何將一個 Node.js 應用程式容器化並執行它。即使您不是 Node.js 開發者,也能輕鬆跟上這個過程。

前置準備

若要跟隨操作,您需要:

  • 一個最新的 Docker 環境
  • 本文 GitHub 倉函式庫隆
  • Node.js(可選)

本文使用的 docker init 命令僅在 Docker Desktop 中可用,但玄貓也會提供其他方案。

首先,克隆 GitHub 倉函式庫

$ git clone https://github.com/nigelpoulton/gsd-book.git

切換到應用程式目錄並檢查檔案:

$ cd gsd-book/node-app
$ ls -l

您應該能看到應用程式的主要檔案,包括 app.jspackage.json 等。

應用程式概述與容器化

這個簡單的 Node.js 應用程式提供了一個基本的網頁介面。容器化的過程包括建立一個 Dockerfile,其中定義瞭如何構建映像檔,然後使用 Docker 命令來構建和執行容器。

Docker Desktop 的 docker init 命令極大地簡化了這個過程,它能夠自動檢測應用程式類別並生成合適的 Dockerfile 和 Docker Compose 檔案。對於不使用 Docker Desktop 的使用者,可以參考倉函式庫範例檔案。

容器化後的應用程式可以輕鬆佈署到任何支援 Docker 的環境中,無需擔心依賴問題或環境差異。這正是容器技術的核心優勢之一:一次構建,到處執行。

探索容器世界

在這個容器化的旅程中,我們學習瞭如何執行、管理和建立容器。從使用命令列到利用 Docker Desktop 的圖形介面,我們體驗了容器技術的便捷和強大。

容器技術徹底改變了應用程式的開發、測試和佈署方式。它讓開發者能夠在任何環境中一致地執行應用程式,減少了「在我的機器上能執行」的問題。透過將應用程式及其依賴封裝在一起,容器提供了可靠的隔離環境,同時保持了輕量級和高效率的特性。

隨著您繼續深入探索,您會發現容器技術的更多高階應用,如微服務架構、容器協調等。這些技術不僅提高了開發效率,還改善了應用程式的可擴充套件性和彈性。

無論您是初學者還是有經驗的開發者,掌握容器技術都將為您的技術工具箱增添一項強大的能力。在軟體開發的現代化程式中,容器已經成為不可或缺的一部分。

Node.js 應用程式容器化實戰:從零開始的 Docker 之旅

在我多年的系統架構設計經驗中,容器化已經從一個選項變成了現代應用程式佈署的標準做法。無論是在創業公司還是大型企業,容器化技術都能顯著提升開發效率及佈署一致性。今天我將帶大家深入瞭解如何將一個簡單的 Node.js 應用程式容器化,即使你不是 Node.js 工作者或 JavaScript 開發者也能輕鬆跟上。

應用程式概觀:理解我們要容器化的專案

我們要容器化的應用程式非常簡潔,僅由四個檔案組成:

  1. app.js - 應用程式的核心程式碼
  2. views/home.pug - 頁面樣板
  3. package.json - 相依套件清單
  4. package-lock.json - 詳細的相依套件版本鎖設定檔

讓我們先看 app.js 的關鍵部分:

// 1. 參照 Express 模組並建立相依性
const express = require('express');
const app = express();

// 2. 設定 Pug 作為樣板引擎
app.set('view engine', 'pug');
app.set('views', './views');

// 應用程式路由設定
app.get('/', (req, res) => {
  res.render('home');
});

// 3. 設定應用程式監聽 8080 埠
app.listen(8080, () => {
  console.log('應用程式執行於 http://localhost:8080/');
});

這個檔案的三個關鍵點是:

  1. 引入 Express 框架作為應用程式的基礎
  2. 設定 Pug 作為 HTML 樣板引擎
  3. 設定應用程式監聽 8080 埠

views/home.pug 檔案包含一些 Pug HTML 語法,用於在應用程式首頁顯示訊息。

package.json 檔案則列出了 Express 和 Pug 作為應用程式的相依套件。至於 package-lock.json,它詳細記錄了應用程式的每一個相依套件及其子相依套件的版本資訊。

相依性蔓延的真實案例

值得注意的是,雖然我們的應用程式只直接依賴兩個套件(Express 和 Pug),但 package-lock.json 卻列出了超過 100 個相依套件!這就是我們常說的「相依性蔓延」(dependency sprawl)。正如開發者常開玩笑說:「我們在建置應用程式時,實際上是在下載整個網際網路。」

這種情況在現代軟體開發中非常普遍:相依套件有自己的相依套件,而這些相依套件又有自己的相依套件,一層向下延伸,形成龐大的相依性網路。容器化技術的一大優勢就是能夠將這些複雜的相依性全部封裝在一個獨立的環境中。

測試應用程式:確認功能正常

在容器化前,我們應該先確認應用程式能正常運作。如果你已安裝 Node.js,可以執行以下步驟:

  1. 安裝應用程式相依套件:
$ npm ci
added 103 packages, and audited 104 packages in 841ms
18 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
  1. 啟動應用程式:
$ node app.js
  1. 開啟瀏覽器並存取 http://localhost:8080/,你會看到應用程式的首頁。

  2. 測試完成後,按 Ctrl-c 終止應用程式,並清理安裝的套件:

$ rm -r node_modules

容器化應用程式:使用 docker init 簡化流程

現在進入本文的核心部分:容器化這個應用程式。容器化(或稱「Dockerizing」)是指將應用程式及其所有相依套件封裝成容器映像檔的過程。

對於容器化新手來說,建立高品質的 Dockerfile 可能是個挑戰,即使是有經驗的 Docker 使用者也可能難以跟上最佳實踐的更新步伐。這時 docker init 命令就派上用場了!

docker init 命令介紹

docker init 是一個相對較新的命令,目前僅在 Docker Desktop 中可用。它能夠讀取現有的應用程式專案,並生成高品質的 Dockerfile、.dockerignore 檔案和 compose.yaml 檔案:

  • Dockerfile 用於 docker build 命令建立容器映像檔
  • .dockerignore 幫助保持映像檔精簡,避免複製不必要的檔案
  • compose.yaml 用於使用 Docker Compose 執行和管理應用程式

如果你沒有使用 Docker Desktop,可以將範例中的 sample-Dockerfile 重新命名為 Dockerfile 來繼續後續步驟。

執行 docker init

在應用程式目錄中執行以下命令:

$ docker init
Welcome to the Docker Init CLI!
Let's get started!
? What application platform does your project use? Node
? What version of Node do you want to use? 23.3.0
? Which package manager do you want to use? npm
? What command do you want to use to start the app? node app.js
? What port does your server listen on? 8080
CREATED: .dockerignore
CREATED: Dockerfile
CREATED: compose.yaml
CREATED: README.Docker.md
!Your Docker files are ready!

按照提示回答問題,生成適合我們應用程式的 Docker 檔案。

檢視生成的 Dockerfile

讓我們來看生成的 Dockerfile 內容(已移除註解並新增行號):

1. ARG NODE_VERSION=23.3.0
2. FROM node:${NODE_VERSION}-alpine
3. ENV NODE_ENV production
4. WORKDIR /usr/src/app
5. RUN --mount=type=bind,source=package.json,target=package.json \
6. --mount=type=bind,source=package-lock.json,target=package-lock.json \
7. --mount=type=cache,target=/root/.npm \
8. npm ci --omit=dev
9. USER node
10. COPY . .
11. EXPOSE 8080
12. CMD node app.js

這個 Dockerfile 實作了多項容器化最佳實踐,讓我來解析每一行的功能:

  1. 第 1-2 行:定義並使用 Node.js 版本,確保以 node:23.3.0-alpine 映像檔作為基礎層
  2. 第 3 行:設定 Node.js 以生產模式執行,以獲得更好的效能
  3. 第 4 行:設定工作目錄為 /usr/src/app,後續指令都將在此目錄執行
  4. 第 5-8 行:使用 mount 繫結實作的依賴項安裝,這種方式能夠最佳化映像檔大小和建構效能
  5. 第 9 行:切換到非 root 使用者 node 執行應用程式,提升安全性
  6. 第 10 行:將應用程式檔案複製到映像檔中(會遵循 .dockerignore 的設定)
  7. 第 11 行:記錄應用程式使用的網路連線埠
  8. 第 12 行:定義容器啟動時執行的命令

這些步驟與我們之前測試應用程式時執行的操作非常相似:安裝依賴項、啟動應用程式等。不同的是,現在這些步驟被編寫為 Dockerfile 指令,以便自動化建立容器映像檔。

建立容器映像檔

現在我們已經瞭解了 Dockerfile 的內容,讓我們使用它來建立容器映像檔:

$ docker build -t node-app .
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.10kB 0.1s
=> [stage-0 1/4] FROM docker.io/library/node:20.8.0-alpine 0.1s
=> CACHED [stage-0 2/4] WORKDIR /usr/src/app 0.0s
=> [stage-0 3/4] RUN --mount=type=bind,source=package.json 0.6s
=> [stage-0 4/4] COPY . . 0.1s
=> exporting to image 0.6s

建立完成後,我們可以確認映像檔是否存在:

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
node-app latest 242fa8fb538b About a minute ago 250MB

測試容器化應用程式

現在讓我們從剛建立的映像檔啟動一個容器,並測試應用程式是否正常運作:

$ docker run -d --name web -p 8080:8080 node-app

這個命令會:

  • 以分離模式(-d)啟動一個容器
  • 將容器命名為 web
  • 將主機的 8080 連線埠對映到容器的 8080 連線埠
  • 使用 node-app 映像檔

現在,開啟瀏覽器並存取 http://localhost:8080/,你應該能看到與之前測試時相同的應用程式頁面,只是這次它執行在容器中!

清理資源

測試完成後,我們可以刪除容器和映像檔:

$ docker rm web -f
web
$ docker rmi node-app
Untagged: node-app:latest
Deleted: sha256:242fa8fb538bb...

映像檔與註冊函式庫入理解 Docker 生態系統

現在我們已成功實作了應用程式的容器化,讓我們更深入地瞭解 Docker 映像檔和註冊函式庫作原理。這些知識對於在多環境佈署和管理容器化應用程式至關重要。

映像檔建構的深層理解

我們在前面使用的 Dockerfile 實作了許多最佳實踐,但為了更清晰地理解映像檔建構的基本原理,讓我們看一個更簡化的 Dockerfile 範例:

FROM node:alpine
WORKDIR /usr/src/app
COPY . .
RUN npm ci --omit=dev
USER node
EXPOSE 8080
CMD ["node", "app.js"]

這個簡化版的 Dockerfile 保留了關鍵功能,但移除了一些複雜的最佳實踐實作。下面是每個指令的詳細解析:

  • FROM node:alpine:指定基礎映像檔。在我多年的容器化經驗中,選擇合適的基礎映像檔對於最終映像檔的大小和安全性至關重要。Alpine Linux 基礎映像檔因其輕量級特性(約 5MB)而廣受歡迎。

  • WORKDIR /usr/src/app:設定工作目錄。這不僅影響後續指令的執行位置,還會在容器執行時設定預設目錄。

  • COPY . .:將應用程式檔案複製到映像檔中。第一個「.」表示主機上的當前目錄,第二個「.」表示映像檔中的當前工作目錄(即 /usr/src/app)。

  • RUN npm ci –omit=dev:安裝生產環境所需的依賴項。npm ci 命令使用 package-lock.json 安裝精確版本的依賴項,而 --omit=dev 選項則跳過開發相依套件,這對於生產環境的映像檔至關重要。

  • USER node:切換到非 root 使用者。這是一個重要的安全實踐,可以降低容器被入侵時的潛在風險。

  • EXPOSE 8080:記錄應用程式使用的網路連線埠。這是一個宣告性指令,它告訴 Docker 容器將在執行時監聽指定的連線埠,但並不會實際釋出該連線埠。

  • CMD [“node”, “app.js”]:定義容器啟動時執行的命令。這裡使用了 exec 形式(JSON 陣列形式),這是推薦的做法,因為它能正確處理訊號傳遞。

映像檔建構的最佳實踐

在我參與的多個企業級容器化專案中,我總結了一些映像檔建構的最佳實踐:

  1. 使用特定版本標籤:避免使用 latest 標籤,而是指定確切的版本,如 node:20.8.0-alpine,以確保建構的一致性。

  2. 多階段建構:對於複雜應用程式,使用多階段

Docker映像檔建構與佈署:從零開始開發容器化應用

在我帶領團隊進行微服務架構轉型的過程中,發現很多開發者對Docker的使用仍停留在基礎階段。映像檔(Image)的建構、多平台支援以及容器登入檔(Registry)的運用,常是團隊成員感到困惑的環節。本文將分享我在實務中累積的經驗,帶你深入理解Docker映像檔的建構、管理與佈署流程,並進一步探討如何使用Docker Compose建構多容器應用。

Dockerfile解析與映像檔架構

在Dockerfile中,每一行指令都對應映像檔建構過程中的特定操作,並最終形成映像檔的層級結構。讓我們解析這些關鍵指令的作用:

  • RUN npm ci --omit-dev:讀取JSON檔案並安裝相依套件到映像檔中,同時排除開發環境的套件,使映像檔更為精簡。
  • USER node:確保應用程式以node使用者而非root身份執行,這是容器安全的最佳實踐之一。
  • EXPOSE 8080:記錄應用程式監聽的網路埠號,雖然這僅是檔案性質的標記,但對於維護者理解容器用途非常有幫助。
  • CMD ["node", "app.js"]:定義容器啟動時執行的指令,這是容器執行的進入點。

當我們完成映像檔建構後,最終會產生一個包含四個層級的映像檔,每個層級都對應Dockerfile中的特定指令。這種層級化結構是Docker最佳化儲存空間和加速佈署的關鍵機制。

建構自訂映像檔

要建構自己的映像檔,我們使用docker build指令。以下是在gsd-book/images目錄中執行的建構過程:

$ docker build -t node-app .
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 144B 0.0s
=> [1/4] FROM docker.io/library/node:alpine... 0.0s
=> CACHED [2/4] WORKDIR /usr/src/app 0.0s
=> [3/4] COPY . . 0.0s
=> [4/4] RUN npm ci --omit=dev 1.8s
=> exporting to image 0.5s

此指令會根據當前目錄中的Dockerfile建構一個名為node-app的映像檔。輸出顯示了建構過程中的每個步驟,這些步驟正是按照Dockerfile中的指令順序執行的。

建構完成後,我們可以使用docker images指令檢視本機上的映像檔:

$ docker images
REPOSITORY  TAG     IMAGE ID      CREATED      SIZE
node-app    latest  73b795fd9bc9  5 seconds ago  250MB

值得注意的是,Docker預設將新建構的映像檔標記為latest標籤。這是Docker的預設行為,除非你在建構時明確指定其他標籤。

容器登入檔的運用與管理

容器登入檔(Registry)是安全儲存映像檔的中央函式庫多數登入檔都遵循OCI分發規範(distribution-spec),確保不同平台間的互操作性。在實務中,我經常使用Docker Hub作為團隊的主要登入檔。

推播映像檔到Docker Hub

要將映像檔推播到Docker Hub,首先需要確保你已登入Docker帳戶:

$ docker login

docker push指令用於推播映像檔到登入檔,但它需要依據映像檔名稱來確定推播目標。因此,我們需要先將本地映像檔重新命名,加入Docker使用者名和目標倉函式庫

$ docker tag node-app nigelpoulton/gsd-book

這個指令將node-app:latest映像檔重新標記為nigelpoulton/gsd-book:latest。注意,這不會建立新的映像檔,只是為同一個映像檔新增了新的名稱,這點可以從兩者擁有相同的映像檔ID看出來:

$ docker images
REPOSITORY            TAG     IMAGE ID      CREATED   SIZE
node-app              latest  73b795fd9bc9  33 mins   250MB
nigelpoulton/gsd-book  latest  73b795fd9bc9  33 mins   250MB

現在,我們可以推播這個映像檔到Docker Hub:

$ docker push nigelpoulton/gsd-book
Using default tag: latest
Push refers to repository [docker.io/nigelpoulton/gsd-book]
2a2799ae89a2: Pushed
4927cb899c33: Pushed

從輸出中我們可以看到,Docker自動使用了latest標籤,並假設我們要推播到Docker Hub (docker.io)。如果需要明確指定這些引數,完整的指令會是:

$ docker push docker.io/nigelpoulton/gsd-book:latest

多架構映像檔的建構

在我帶領的跨國團隊中,開發環境的多樣性常導致映像檔相容性問題。例如,開發者可能使用ARM架構的Mac電腦,而生產環境則執行在AMD/x86架構上。這就導致開發者在本地建構的映像檔無法在生產環境中正常執行。

Docker提供瞭解決此問題的方案,允許我們建構支援多種平台的映像檔。使用--platform引數,我們可以指定映像檔支援的平台:

$ docker build \
--platform=linux/amd64,linux/arm64 \
-t nigelpoulton/gsd-book:latest \
--push --no-cache .

這個指令會建構兩個映像檔:一個用於Linux/AMD64平台,另一個用於Linux/ARM64平台,並直接推播到Docker Hub。如果遇到多平台建構不支援的錯誤,可以執行以下指令建立新的builder並設為預設:

$ docker buildx create --driver=docker-container --name=container
$ docker buildx use container

建構完成後,Docker Hub上的同一倉函式庫含兩個不同架構的映像檔。Docker足夠人工智慧,能夠自動提取與你係統架構相符的映像檔版本。

使用Docker Compose佈署多容器應用

在實際的生產環境中,大多數應用程式都由多個容器協同工作組成。這些多容器應用或微服務應用的管理,可能會變得相當複雜。在玄貓帶領的微服務轉型專案中,我始終推薦使用Docker Compose來簡化這一過程。

應用概述

讓我們看一個包含兩個容器的應用範例:

  1. web容器:執行Python Flask應用,顯示一個簡單的網頁,包含圖片、文字和計數器
  2. store容器:執行Redis資料函式庫儲網頁重新整理次數

當使用者重新整理頁面時,計數器會增加並儲存在Redis資料函式庫網頁會顯示當前的計數值。

Compose檔案解析

Docker Compose使用YAML格式的設定檔案來定義多容器應用。在我們的範例中,compose.yaml檔案定義了兩個容器和一個網路:

networks:
  internal:
services:
  web:
    build: .
    command: python app.py
    ports:
      - "5555:8080"
    networks:
      - internal
  store:
    image: redis:alpine
    networks:
      - internal

讓我深入解析這個設定檔案的結構:

  1. networks 區塊定義了一個名為internal的網路,所有容器將連線到這個網路以便相互通訊
  2. services 區塊定義了應用中的容器:
    • web服務
      • build: . 告訴Docker使用當前目錄中的Dockerfile建構映像檔
      • command: python app.py 指定容器啟動時執行的命令
      • ports: - "5555:8080" 將容器內的8080連線埠對映到主機的5555連線埠
      • networks: - internal 將容器連線到internal網路
    • store服務
      • image: redis:alpine 指定使用redis:alpine映像檔
      • networks: - internal 將容器連線到internal網路

web容器與store容器的連線是在應用程式碼中定義的,而非Compose檔案。在app.py中,可以看到web服務透過store主機名連線到Redis服務的6379連線埠:

import time
import redis
from flask import Flask, render_template
app = Flask(__name__)
cache = redis.Redis(host='store', port=6379)

這是Docker網路的強大之處:在同一網路中的容器可以透過服務名稱直接相互存取。

佈署多容器應用

要佈署這個應用,首先克隆GitHub倉函式庫

$ git clone https://github.com/nigelpoulton/gsd-book.git

然後進入gsd-book/compose-app目錄:

$ cd gsd-book/compose-app
$ ls -l
-rw-r--r--@ 1 nigelpoulton staff 528 Dockerfile
-rw-r--r--@ 1 nigelpoulton staff 594 app.py
-rw-r--r--@ 1 nigelpoulton staff 283 compose.yaml
-rw-r--r--@ 1 nigelpoulton staff 18 requirements.txt
drwxr-xr-x 5 nigelpoulton staff 160 static
drwxr-xr-x 4 nigelpoulton staff 128 templates

使用docker compose up命令佈署應用:

$ docker compose up

這個命令會讀取compose.yaml檔案,建立網路,建構web服務的映像檔,提取redis:alpine映像檔,並啟動兩個容器。如果想在背景執行應用,可以新增-d引數:

$ docker compose up -d

管理多容器應用

Docker Compose提供了一系列命令來管理多容器應用的整個生命週期:

  1. 檢視應用狀態:

    $ docker compose ps
    
  2. 檢視應用日誌:

    $ docker compose logs
    

    也可以檢視特定服務的日誌:

    $ docker compose logs web
    
  3. 停止應用但保留資源:

    $ docker compose stop
    
  4. 啟動已停止的應用:

    $ docker compose start
    
  5. 完全移除應用及其資源:

    $ docker compose down
    

在我帶領的微服務架構轉型專案中,這些命令成為團隊日常開發和測試的基礎工具。特別是docker compose up -ddocker compose down命令,極大簡化了本地開發環境的啟動和清理過程。

AI模型登入檔

在容器技術快速發展的同時,AI技術也在迅速崛起。我們現在將訓練好的AI模型儲存在模型登入檔中,概念上類別似於容器登入檔。這使得AI模型的存取和使用變得更加簡便。

目前流行的模型登入檔包括Hugging Face、Ollama函式庫eptune.ai等。這些平台允許開發者輕鬆推播和提取AI模型,正如我們操作Docker映像檔一樣。

在未來的開發實踐中,我們將看到容器技術與AI技術的更深度融合。例如,佈署AI聊天機器人應用時,可以從Ollama模型函式庫Mistral 7B AI模型,並在容器內執行它。這種融合為開發者提供了強大的工具組合,使複雜AI應用的佈署變得前所未有的簡單。

容器化實踐的關鍵經驗

透過本文的探討,我們深入理解了Docker映像檔的建構機制、容器登入檔的使用,以及Docker Compose在多容器應用佈署中的強大功能。以下是我在實際專案中總結的幾點關鍵經驗:

  1. 映像檔分層設計 - 理解並善用Docker映像檔的分層特性,可以大幅提高映像檔的重用率和構建效率。當我們修改應用程式碼時,只有相關層會被重新構建。

  2. 多架構支援考量 - 在異構團隊中,提前規劃多架構支援至關重要。使用--platform引數構建多架構映像檔,可以確保開發到生產的平滑過渡。

  3. 容器間通訊設計 - 在設計多容器應

使用 Docker Compose 佈署與管理多容器應用程式

啟動應用程式

在開發或佈署多容器應用程式時,Docker Compose 提供了一種極為便捷的方式來定義、啟動和管理所有相關容器。要啟動我們的應用程式,只需執行以下命令:

docker compose up --detach

這個命令會讀取當前目錄中的 compose.yaml 檔案並佈署其中定義的所有元素。執行後,會看到類別似這樣的輸出:

[+] store Pulling...
[+] Building...
...
[+] Running 3/3
- Network compose-app_internal Created 0.0s
- Container compose-app-store-1 Started 0.1s
- Container compose-app-web-1 Started 0.0s

從輸出可以看出,Docker 首先提取了 store 容器的映像檔,然後建構了 web 容器的映像檔。接著建立內部網路,啟動兩個容器,並自動將這些容器連線到內部網路,同時完成所有的埠對映工作。

要檢視應用程式中的容器,可以執行:

docker compose ps

輸出會顯示兩個執行中的容器:

NAME                SERVICE   IMAGE               PORTS
compose-app-store-1 store     redis:alpine        6379/tcp
compose-app-web-1   web       compose-app-web     0.0.0.0:5555->8080

這裡有一個值得注意的重要細節:Docker 自動為容器名稱加上了當前工作目錄名稱(「compose-app」)作為字首。這是因為 Compose 使用目前目錄名作為專案名稱,幫助開發者區分不同的 Compose 專案。

確認內部網路是否已建立:

docker network ls

甚至連網路名稱也會加上專案名稱作為字首:

NETWORK ID     NAME                   DRIVER    SCOPE
63e63ec84ae1   bridge                 bridge    local
0c55ba331291   compose-app_internal   bridge    local
9b4509c1b8a4   host                   host      local
86afd80b6872   none                   null      local

應用程式測試與管理

如果使用 Docker Desktop,可以透過瀏覽器連線 http://localhost:5555/ 並多次點選重新整理按鈕,計數器會隨每次重新整理而增加。

如果使用 Multipass,則需要連線到 Multipass 虛擬機器的 IP 地址和 5555 埠。可以透過在虛擬機器外執行 multipass list 取得 IP 地址。

管理應用程式生命週期

Docker Compose 不僅可以佈署應用程式,還提供了一系列生命週期管理操作,如停止、重啟和刪除應用程式。更新通常透過修改 compose.yaml 檔案並重新執行 docker compose up 來完成。

以下是常見的管理操作:

停止應用程式

首先檢查應用程式狀態:

docker compose ls

然後停止應用程式:

docker compose stop

停止後,瀏覽器重新整理將會逾時。

重啟應用程式

重新啟動應用程式:

docker compose restart

重新整理瀏覽器後,應用程式還原執行,與計數器不會重置。這是因為容器僅被停止,而停止的容器不會丟失資料。

更新應用程式

Compose 應用程式是宣告式的,意味著更新應透過修改 compose.yaml 檔案然後重新佈署應用程式來完成。

例如,修改 web 服務的設定,將 build: . 替換為使用特定映像檔:

services:
  web:
    # build: .  # 刪除這一行
    image: nigelpoulton/gsd-book:banner  # 新增這一行
    command: python app.py
    # 其餘保持不變

儲存變更後,執行以下命令更新應用程式:

docker compose up --pull always --detach

這個命令會讀取更新後的 compose.yaml 檔案並進行必要的更新。由於只修改了 web 服務,Docker 只會變更該服務而保持 store 服務不變。--pull always 標誌會強制 Docker 提取兩個服務的最新映像檔。

重新整理瀏覽器後,網頁會顯示變更(新增了橫幅),與計數器不會重置。這展示了微服務架構的優勢—只需更新特定服務,而無需重啟整個應用程式。

清理應用程式

刪除應用程式及相關資源:

docker compose down --rmi all

此命令會刪除容器、網路和映像檔。--rmi all 標誌確保刪除與應用程式相關的所有映像檔。

Docker 與 AI

人工智慧(AI)已無處不在,企業越來越多地使用 Docker 來開發和佈署本地 AI 應用程式。接下來,玄貓將帶領大家使用 Docker Compose 在本地電腦上佈署 AI 聊天機器人,並使用它來完成簡單的編碼任務,最後檢視應用程式的 AI 設定。

前置需求

若要佈署聊天機器人並跟隨教學,需要:

  1. Docker(建議 Docker Desktop 4.36 或更新版本)
  2. Docker Compose(包含在 Docker Desktop 中)
  3. 至少 8GB RAM 分配給 Docker(建議 16GB)
  4. 至少 12GB 可用磁碟空間

Docker Desktop 使用者可以透過點選 Docker 鯨魚圖示,選擇 Settings > Resources,移動 Memory limit 和 Virtual disk limit 滑塊來分配記憶體和磁碟空間,然後點選 Apply & restart。

聊天機器人可使用 GPU 或普通 CPU。GPU 會提高速度,但需要具有 NVIDIA GPU 和安裝 NVIDIA 驅動程式的 Docker 主機。

聊天機器人概述

這個 AI 聊天機器人是一個多層應用程式,包含三個元件:

  1. 聊天介面
  2. 後端 API
  3. 模型伺服器

「模型」是 AI 應用程式的技術語,表示模型伺服器是 AI 智慧執行的地方。在這個範例中,模型伺服器使用 Ollama 執行 Mistral AI 模型的副本。

聊天機器人架構包括:

  • 聊天介面:實作為在埠 3000 上的 Web 服務
  • 後端 API 伺服器:與聊天介面通訊,並將提示轉發給模型伺服器
  • 模型伺服器:使用 Ollama 在埠 11434 上提供 AI 模型的本地例項

應用程式在 Compose 檔案中定義如下:

services:
  frontend:
    image: nigelpoulton/gsd-book:chat-frontend
    build: ./frontend
    ports:
      - "3000:3000"
    command: npm run start
    depends_on:
      - backend

  backend:
    image: nigelpoulton/gsd-book:chat-backend
    build: ./backend
    ports:
      - "8000:8000"
    environment:
      - MODEL_HOST=http://ollama:11434
    depends_on:
      ollama:
        condition: service_healthy

  ollama:
    image: nigelpoulton/gsd-book:chat-model
    build: ./ollama
    ports:
      - "11434:11434"
    volumes:
      - ollama_data:/root/.ollama
    environment:
      - MODEL=${MODEL:-mistral:latest}
volumes:
  ollama_data:
    name: ollama_data

檔案定義了三個服務和一個卷。服務分別是 frontend、backend 和 ollama,對應架構中的三個應用程式元件。MODEL 環境變數告訴模型伺服器提取並使用最新的 Mistral AI 模型。可以修改此變數以強制模型伺服器使用 Ollama 模型函式庫其他模型,例如更新為 llama3.2:latest 以使用最新的 Llama 3.2 模型。

佈署與測試聊天機器人

首先克隆 GitHub 儲存函式庫

git clone https://github.com/nigelpoulton/gsd-book

進入 gsd-book/ai/chatbot/ 目錄並檢查 Compose 檔案是否存在:

ls -l

如果 Docker 主機啟用了 GPU 支援,使用以下命令啟動聊天機器人:

docker compose --file gpu-compose.yaml up --detach

如果沒有 GPU 或不確定,使用以下命令:

docker compose up --detach

首次佈署時,Docker 會提取容器映像檔,Ollama 會提取 AI 模型。AI 模型有幾個 GB 大小,可能需要一段時間下載。如果網路連線較慢,甚至可能逾時並顯示 chatbot-ollama-1 is unhealthy 訊息。如果發生這種情況,可以嘗試再次啟動應用程式,或編輯 Compose 檔案增加 ollama 服務的 healthcheck.start_period 欄位值。

Docker Compose 讓佈署和管理多容器應用變得簡單而高效。無論是簡單的 Web 應用還是複雜的 AI 系統,都能以宣告式方法快速佈署和管理,大提升了開發和維運效率。

在本地佈署並使用AI聊天機器人:實戰

確認應用程式狀態

首先,我們需要確認聊天機器人應用程式的執行狀態。使用以下指令檢查:

$ docker compose ls
NAME     STATUS        CONFIG FILES
chatbot  running(3)    ~/gsd-book/chatbot/compose.yaml

當三個服務都顯示為「running」狀態時,代表聊天機器人已成功啟動。接下來,在瀏覽器中存取 http://localhost:3000 並嘗試提問,測試聊天機器人的功能是否正常。

聊天機器人的效能考量

使用聊天機器人時,可能會看到「Thinking」訊息,表示AI正在處理您的問題。如果機器人偶爾無法回應,這通常是因為硬體資源限制造成的,特別是當您在CPU環境或僅有8GB記憶體的裝置上執行時。

若要提升效能,可以:

  1. 編輯Compose檔案中的ollama.deploy.resources.limits.memory欄位,增加模型伺服器的記憶體設定
  2. 重新啟動聊天機器人
  3. 必要時增加Docker本身的記憶體分配(如果Docker僅有8GB記憶體,為模型伺服器分配16GB是沒有意義的)

與AI聊天機器人協作完成程式設計任務

在這個部分,玄貓將展示如何與AI聊天機器人協同工作,完成一個簡單的程式設計任務。即使按照相同的範例操作,由於不同版本的模型和聊天機器人的回答差異,您可能會得到不同的結果。

範例任務:建立顯示「Docker is great for AI」的網頁伺服器

我向聊天機器人提出的具體需求是:「編寫一個簡單的Node.js網頁應用,在5005連線埠監聽,在藍色背景上居中顯示白色文字’Docker is great for AI’,並將其容器化。」

聊天機器人的回應包含三個步驟(為了適應篇幅,已精簡內容):

步驟1:建立Node.js應用程式
const express = require('express');
const app = express();
app.get('/', (req, res) => {
  res.send(`<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <style>
    body {
      background-color: #0096FF;
      color: white;
    }
  </style>
</head>
<body>
  Docker is great for AI
</body>
</html>`);
});
app.listen(5005, () => {
  console.log('App listening on port 505!');
});
步驟2:建立Dockerfile
FROM node:14-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5005
CMD [ "node", "app.js" ]
步驟3:構建與執行容器
docker build -t my-node-app:latest .
docker run -p 5005:5005 --name my-node-app my-node-app

完成後,可以透過瀏覽器存取 http://localhost:5005 檢視應用。

實際操作與問題解決

按照指示,我將程式碼儲存為app.js,並在同一目錄中建立Dockerfile。最終在新的app目錄中有兩個檔案:

  • app.js
  • Dockerfile

執行構建命令成功,但執行容器時遇到了問題:Cannot find module 'express'錯誤。

我向聊天機器人詢問這個錯誤的可能原因,它回答:

  1. 該錯誤表示Node.js找不到’express’包,可能是因為未安裝該包或package.json檔案中未列出該依賴。
  2. 解決方案:執行npm install express安裝該包,或在package.json中新增"express": "^4.17.1"並執行npm install

仔細檢查後發現,Dockerfile中嘗試複製package.json檔案並執行npm install,但專案中並沒有這個檔案。於是我建立了一個package.json檔案:

{
  "dependencies": {"express": "^4.21.1"}
}

重新執行docker build和docker run命令後,應用成功啟動,瀏覽器存取http://localhost:5005顯示了預期的網頁。

AI聊天機器人的侷限性

透過這個例子,我們可以看到聊天機器人雖然有用但並不完美:

  1. 沒有明確說明主應用檔案的名稱,我必須從後續指令中推斷
  2. 沒有提到需要建立package.json檔案
  3. 在故障排除時建議使用已過時六年的Express版本(v4.17.1)

這提醒我們,雖然AI聊天機器人可以提供幫助,但仍需要人類的判斷和專業知識來彌補其不足。

深入檢查聊天機器人

我們前面提到,模型伺服器容器使用Ollama執行本地Mistral AI模型例項。現在讓我們連線到模型伺服器容器並檢查Ollama和模型設定。

使用以下命令開啟模型伺服器容器的shell工作階段:

$ docker exec -it chatbot-ollama-1 sh

當提示符變為”#“時,表示您已成功登入容器,可以開始檢查Ollama安裝情況。

在我多年與AI工具協作的經驗中,這種以容器方式佈署的本地AI解決方案特別適合對資料隱私有嚴格要求的場景,或者需要在不依賴外部API的情況下執行AI應用的環境。透過Docker容器化技術,我們可以快速佈署、擴充套件和管理這些AI服務,同時保持環境的一致性和可重現性。

這種方式不僅降低了AI應用的佈署門檻,也為開發者提供了一個安全、可控的環境來實驗和改進AI功能,而不必擔心網路連線、API限制或資料外洩等問題。

Ollama:讓AI大模型在你電腦上飛起來

近年來,大語言模型(LLM)在AI領域掀起了一場革命,但大多數人只能透過雲端服務使用這些強大的模型。而Ollama徹底改變了這個情況,它讓我們能夠在自己的電腦上執行如Mistral和Gemma等先進AI模型。

在這篇文章中,我將分享如何使用Ollama佈署、管理和執行本地AI模型,以及如何透過Docker建構一個完整的AI聊天機器人系統。

Ollama的核心功能與架構

Ollama是一個執行時環境,專為在本地電腦上執行AI模型而設計。它的核心價值在於讓開發者能夠在不依賴雲端服務的情況下,直接在本地機器上佈署和使用大語言模型。

當你啟動根據Ollama的聊天機器人時,它會執行以下關鍵步驟:

  1. 啟動Ollama作為背景服務
  2. 將AI模型(如Mistral)下載到容器中
  3. 在需要時啟動模型並保持在記憶體中

讓我們從檢查Ollama的版本開始:

# ollama --version
ollama version is 0.4.0-rc8

Ollama服務執行在背景,我們可以使用ps命令來確認:

$ ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 18:08 ? 00:00:00 /bin/bash /usr/loc
sh
root 7 1 0 18:08 ? 00:00:00 /bin/ollama serve

從輸出中可以看到,PID 1顯示啟動Ollama的指令碼位置,而PID 7顯示Ollama程式正在serve模式下執行。

管理本地AI模型函式庫Ollama的強大之處在於它能夠管理多個本地AI模型。讓我們檢查目前系統上有哪些模型:

# ollama ls
NAME ID SIZE MODIFIED
mistral:latest f974a74358d6 4.1 GB 6 days ago
gemma2:latest ff02c3702f32 5.4 GB 6 days ago

這個輸出顯示系統上有兩個模型:Mistral和Gemma2。每個模型都超過4GB,它們儲存在掛載到模型伺服器容器上的ollama_data卷中。這就是為什麼佈署Ollama前需要確保有足夠的磁碟空間。

模型執行狀態監控

想知道哪些模型正在執行嗎?使用ollama ps命令:

# ollama ps
NAME ID SIZE PROCESSOR UNTIL
mistral:latest f974a74358d6 5.9 GB 100% CPU 4 minutes

如果沒有看到任何活動模型,這是因為模型只有在與它互動時才會被呼叫,並且在記憶體中保持執行5分鐘。如果模型未處於活動狀態,可以回到聊天機器人的網頁介面,問一個簡單的問題來啟用它。

深入瞭解模型細節

要檢視特定模型的詳細資訊,可以使用ollama show命令:

# ollama show mistral
Model
architecture llama
parameters 7.2B
context length 32768
embedding length 4096
quantization Q4_0
Parameters
stop "[INST]"
stop "[/INST]"
License
Apache License
Version 2.0, January 2004

從輸出中可以看到,Mistral是根據Llama架構的7.2B引數模型,具有32768的連貫的背景與環境長度。這種資訊對於理解模型的能力和限制非常有用。

在我的實踐中,較大連貫的背景與環境長度的模型在處理複雜程式設計任務時表現更佳,因為它們能夠「記住」更多的程式碼和討論內容。不過這也意味著它們需要更多的系統資源。

使用Docker Compose佈署AI聊天機器人

在前面的章節中提到,我們可以使用Docker Compose來佈署一個完整的AI聊天機器人系統。這個系統包含三個主要元件:

  1. 前端介面(使用者互動層)
  2. 後端服務(處理請求和回應)
  3. 模型伺服器(執行Ollama和AI模型)

這種三層架構設計使得系統更加模組化和可擴充套件。當使用者與前端介面互動時,請求會被傳送到後端服務,後端服務再與模型伺服器通訊以取得AI的回應。

清理與管理系統資源

當你完成使用AI聊天機器人後,正確清理資源是一個好習慣。根據你的需求,有兩種清理方式:

如果你計劃在不久的將來再次執行AI聊天機器人,可以使用以下命令停止它,但保留其卷和映像以便更快地重啟:

$ docker compose down

如果你不打算再次執行它,可以用以下命令停止應用並刪除其卷和映像:

$ docker compose down --volumes --rmi all

輸出將顯示所有被移除的容器、映像和網路:

[+] Running 8/8
- Container chatbot-frontend-1 Removed
- Container chatbot-backend-1 Removed
- Container chatbot-ollama-1 Removed
- Image nigelpoulton/gsd-book:chat-frontend Removed
- Image nigelpoulton/gsd-book:chat-backend Removed
- Image nigelpoulton/gsd-book:chat-model Removed
- Volume ollama_data Removed
- Network chatbot_default Removed

別忘了移除任何作為你在聊天機器人幫助下完成的額外編碼任務的一部分建立的容器和映像。

AI本地佈署的下一步

完成了Ollama和Docker的基礎學習後,你可以根據自己的角色和興趣選擇不同的發展方向:

對於開發者

如果你是開發者,你現在已經準備好開始容器化應用程式,包括LLM和其他生成式AI專案。玄貓建議深入學習如何最佳化模型引數,以及如何將Ollama整合到現有的開發工作流程中。

對於DevOps工程師

對於DevOps或其他xOps角色,你可以開始在工作專案中使用Docker,並探索如何將AI模型佈署納入CI/CD流程。自動化模型更新和版本控制是值得深入研究的課題。

對於架構師和管理階層

如果你是架構師或處於管理階層,你現在擁有足夠的知識開始做出明智的決策。評估在組織內佈署本地AI模型的成本效益,以及如何平衡雲端AI服務與本地佈署的優缺點。

實踐建議

無論你的角色是什麼,都應該盡可能多地進行實踐。Docker Desktop和Ollama是開始的絕佳工具。參加當地的Docker和雲原生(CNCF)聚會也是取得知識和擴充套件人脈的好方法。

探索更多學習資源

如果你希望繼續Docker之旅,可以考慮以下資源:

  • 深入Docker的細節,可以閱讀《Docker Deep Dive》
  • 想了解Kubernetes,可以查閱《Quick Start Kubernetes》
  • 對AI在時事和未來的更廣泛影響感興趣,可以參考《AI Explained: Facts, Fiction, and Future》

此外,網上有大量影片課程可供學習,如pluralsight.com上的Docker和雲原生技術課程。

在技術快速發展的今天,持續學習和實踐是掌握新技術的關鍵。透過Ollama和Docker,你已經邁出了在本地佈署和管理AI模型的第一步,這為你探索AI技術的更多可能性開啟了大門。

在我使用Ollama的經驗中,將它與現有的開發工具和工作流程整合是提高生產力的關鍵。例如,你可以建立一個本地開發環境,其中包含程式碼編輯器、版本控制、容器化應用和Ollama提供的AI輔助功能,形成一個強大的開發生態系統。

無論你選擇哪條路徑,記住技術是為瞭解決問題而存在的。專注於你想解決的實際問題,然後利用這些工具來實作你的目標。

Docker 容器化實戰:從環境準備到應用佈署

前置準備與環境設定

在開始容器化之旅前,需要先確保環境已正確設定。以下是完成本教學所需的基本工具與資源:

  • Docker Desktop 4.36 或更高版本
  • 至少 8GB 記憶體與 8GB 硬碟空間分配給 Docker(建議 16GB)
  • 一個 Docker 帳號
  • 本教學的 GitHub 程式碼函式庫首先,讓我們clone教學所需的程式碼函式庫
git clone https://github.com/nigelpoulton/gsd-book.git
Cloning into 'gsd-book'...
remote: Enumerating objects: 53, done.
remote: Counting objects: 100% (53/53), done.
remote: Compressing objects: 100% (36/36), done.
remote: Total 53 (delta 18), reused 43 (delta 10), pack-reused 0
Receiving objects: 100% (53/53), 52.97 KiB | 763.00 KiB/s, done.
Resolving deltas: 100% (18/18), done.

cd gsd-book

完成這些步驟後,你就準備好開始進行實際操作了。

容器基礎操作實戰

在進入容器化應用程式之前,讓我們先檢查系統上現有的映像檔和容器:

docker images
REPOSITORY TAG IMAGE ID CREATED SIZE

docker ps
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES

現在,讓我們建立一個名為 test 的容器,根據 nigelpoulton/gsd-book 映像檔:

docker run -d --name test -p 5555:8080 nigelpoulton/gsd-book
Unable to find image 'nigelpoulton/gsd-book:latest' locally
latest: Pulling from nigelpoulton/gsd-book
ee605d576664: Download complete
4f4fb700ef54: Download complete
53062d7cc4ed: Download complete
bdb2de7ba06c: Download complete
56b352bc4d6a: Download complete
Digest: sha256:262ad15072366133dc...7290cef95634f237556056
Status: Downloaded newer image for nigelpoulton/gsd-book:latest
f879f4b0571022dad8197be14db222ef75b5280f4b02a804632d30b8381bb88d

上述指令透過以下方式運作:

  • -d 引數讓容器在背景執行
  • --name test 為容器指定名稱
  • -p 5555:8080 將主機的 5555 埠對映到容器的 8080 埠

在瀏覽器中存取 http://localhost:5555/ 來測試容器是否正常運作。接著,我們可以嘗試停止、重啟和刪除容器:

# 停止容器(可能需要 10 秒來優雅關閉)
docker stop test

# 重啟容器
docker start test

# 強制刪除容器
docker rm test -f

將應用程式容器化

在這部分,我們將學習如何將一個 Node.js 應用程式容器化。請確保你已切換到 gsd-book/node-app 資料夾。

首先,讓我們檢視應用程式檔案:

ls -l
-rw-r--r-- 1 nigelpoulton staff 341 app.js
-rw-r--r-- 1 nigelpoulton staff 36757 package-lock.json
-rw-r--r-- 1 nigelpoulton staff 357 package.json
drwxr-xr-x 3 nigelpoulton staff 96 views

接著使用 Docker 的 init 指令來自動生成容器化所需的檔案:

docker init
Welcome to the Docker Init CLI!
Let's get started!
? What application platform does your project use? Node
? What version of Node do you want to use? 23.3.0
? Which package manager do you want to use? npm
? What command do you want to use to start the app? node app.js
? What port does your server listen on? 8080
CREATED: .dockerignore
CREATED: Dockerfile
CREATED: compose.yaml
CREATED: README.Docker.md
!Your Docker files are ready!

這個互動式指令會根據你的回答生成適合的 Dockerfile 和其他設定檔案。接著,我們使用生成的 Dockerfile 建構映像檔:

docker build -t node-app .
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.10kB 0.1s
=> [stage-0 1/4] FROM docker.io/library/node:20.8.0-alpine 0.1s
=> CACHED [stage-0 2/4] WORKDIR /usr/src/app 0.0s
=> [stage-0 3/4] RUN --mount=type=bind,source=package.json 0.6s
=> [stage-0 4/4] COPY . . 0.1s
=> exporting to image 0.6s
<Snip>
View build details: docker-desktop://dashboard/build/...

docker build 指令會根據 Dockerfile 中的指示建構映像檔,-t node-app 引數為映像檔指定標籤名稱。建構完成後,讓我們確認映像檔已建立:

docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
node-app latest 242fa8fb538b About a minute ago 250MB

現在,我們可以根據這個映像檔執行容器:

docker run -d --name web -p 8080:8080 node-app

在瀏覽器中存取 http://localhost:8080/ 來測試應用程式。測試完成後,我們可以刪除容器和映像檔:

# 刪除容器
docker rm web -f
web

# 刪除映像檔
docker rmi node-app
Untagged: node-app:latest
Deleted: sha256:242fa8fb538bb...

透過這個實作,我們完成了從環境準備、基本容器操作到將應用程式容器化的完整流程。Docker 的 init 指令極大地簡化了容器化過程,自動生成適合的 Dockerfile,讓開發者能夠專注於應用程式本身而非容器化的複雜細節。

容器化不僅使應用程式佈署更加一致和可靠,還提供了環境隔離,確保應用程式在不同環境中都能以相同方式執行。這個實作展示了從開發到佈署的容器化工作流程,為更複雜的容器化應用奠定了基礎。

Docker 容器技術實戰:映象管理與多容器應用佈署

Docker 映象建構與管理

在現代軟體開發中,Docker 已成為容器化應用程式的標準工具。透過 Docker,我們可以將應用程式及其依賴項封裝成標準化的映象,確保在不同環境中的一致性執行。接下來,玄貓將帶領大家深入瞭解 Docker 映象的建構、管理以及多容器應用的佈署。

解析 Dockerfile 結構

首先,我們來看一個基本的 Dockerfile:

FROM node:alpine
WORKDIR /usr/src/app
COPY . .
RUN npm ci --omit=dev
USER node
EXPOSE 8080
CMD ["node", "app.js"]

內容解密

  • FROM node:alpine:使用 Node.js 的 Alpine Linux 版本作為基礎映象,這是一個輕量級的 Linux 發行版
  • WORKDIR /usr/src/app:設定容器內的工作目錄
  • COPY . .:將當前目錄的所有檔案複製到容器的工作目錄
  • RUN npm ci --omit=dev:執行 npm 安裝,但排除開發依賴,減少映象大小
  • USER node:切換到低許可權使用者 node 執行應用程式,提高安全性
  • EXPOSE 8080:宣告應用程式使用的埠號
  • CMD ["node", "app.js"]:容器啟動時執行的命令

建構與標記 Docker 映象

建構映象時,我們使用 docker build 命令:

docker build -t node-app .

這個命令會根據當前目錄的 Dockerfile 建構一個名為 node-app 的映象。建構完成後,我們可以使用 docker images 檢視:

docker images

輸出結果顯示我們的 node-app 映象已建立,大小約 250MB。

若要將映象上載到 Docker Hub,首先需要標記映象以符合 Docker Hub 的命名規範:

docker tag node-app 你的使用者名/gsd-book

這個命令將 node-app 映象重新命名為 你的使用者名/gsd-book,接著就可以推播到 Docker Hub:

docker push 你的使用者名/gsd-book

多平台映象建構與推播

在異質運算環境中,我們常需要為不同 CPU 架構建構映象。Docker 提供了一個簡便的方法同時建構多架構映象:

docker build \
  --platform=linux/amd64,linux/arm64 \
  -t 你的使用者名/gsd-book:latest \
  --push --no-cache .

內容解密

  • --platform=linux/amd64,linux/arm64:指定建構 AMD64 和 ARM64 兩種架構的映象
  • -t 你的使用者名/gsd-book:latest:標記映象名稱和標籤
  • --push:建構完成後自動推播到 Docker Hub
  • --no-cache:不使用快取,確保映象完全重建

這種方法特別適合需要同時支援 x86 伺服器和 ARM 裝置(如 Apple M 系列晶片或 Raspberry Pi)的應用程式。

多容器應用程式佈署

對於較複雜的應用程式,通常需要多個容器協同工作。Docker Compose 是管理多容器應用程式的絕佳工具,讓我們透過一個實際案例深入瞭解。

分析應用程式結構

首先,讓我們檢視專案的檔案結構:

ls -l

專案包含以下關鍵檔案:

  • Dockerfile:定義 Web 應用程式的容器
  • app.py:Python Web 應用程式碼
  • compose.yaml:Docker Compose 設定檔案
  • requirements.txt:Python 依賴項
  • statictemplates 資料夾:Web 應用程式資源

使用 Docker Compose 啟動應用程式

使用以下命令啟動整個應用程式:

docker compose up --detach

該命令會讀取 compose.yaml 檔案,建立並啟動所有定義的服務。--detach 引數使容器在背景執行。

啟動後,我們可以檢視應用程式中的容器:

docker compose ps

輸出顯示應用程式包含兩個服務:

  • store:使用 Redis 的 Alpine 版本
  • web:Web 應用程式,對外暴露 5555 埠,內部使用 8080 埠

我們還可以檢視為應用程式建立的網路:

docker network ls

可以看到 Docker Compose 自動建立了一個名為 compose-app_internal 的網路,用於容器間通訊。

管理 Docker Compose 應用程式

Docker Compose 提供了多種命令管理應用程式生命週期:

# 停止應用程式
docker compose stop

# 重新啟動應用程式
docker compose restart

修改與更新應用程式

我們可以修改 compose.yaml 檔案以更新應用程式設定。例如,將 Web 服務的映象從本地建構改為使用 Docker Hub 上的預建映象:

networks:
  internal:
services:
  web:
    # build: .  刪除此行
    image: nigelpoulton/gsd-book:banner  # 新增此行
    command: python app.py
    # ... 其餘設定 ...

然後使用以下命令更新應用程式:

docker compose up --pull always --detach

內容解密

  • --pull always:始終提取最新映象,確保使用最新版本
  • --detach:在背景執行容器

清理應用程式資源

當不再需要應用程式時,可以使用以下命令完全移除應用程式及相關資源:

docker compose down --rmi all

內容解密

  • down:停止並移除容器、網路
  • --rmi all:同時移除所有應用程式使用的映象

這個命令會徹底清理應用程式的所有元件,包括容器、網路和映象,釋放系統資源。

Docker 容器化工作流程的最佳實踐

在玄貓多年容器化實踐中,發現以下幾點是建構高效 Docker 工作流程的關鍵:

  1. 輕量級基礎映象:優先選擇 Alpine 或 Distroless 映象,減少攻擊面並提高效能

  2. 多階段建構:對於複雜應用程式,使用多階段建構分離建構環境與執行環境

  3. 非 root 使用者:在 Dockerfile 中使用 USER 指令切換到非特權使用者執行應用程式

  4. 版本控制:明確標記映象版本,避免使用 latest 標籤於生產環境

  5. 網路隔離:在 Docker Compose 中適當定義網路,確保服務間的隔離與安全通訊

  6. 環境變數管理:使用 .env 檔案或外部密碼管理系統處理敏感設定

這些實踐不僅可以提高容器化應用程式的安全性和可靠性,還能簡化開發團隊的協作流程。

Docker 的容器化技術徹底改變了應用程式的開發、測試和佈署方式。透過本文介紹的映象管理和多容器應用佈署技術,開發者可以建立更加一致、可靠與易於管理的應用程式生態系統。無論是單體應用程式還是微服務架構,Docker 都為現代應用程式提供了強大的基礎設施支援。

Docker 與人工智慧的結合:容器化 AI 應用實戰

在現代軟體開發中,Docker 與人工智慧的結合正成為一個強大的技術組合。本文將探討如何使用 Docker 來佈署和管理 AI 應用,特別是以聊天機器人為例,展示如何透過容器化技術簡化 AI 模型的佈署過程。

使用 Docker Compose 佈署 AI 聊天機器人

首先,讓我們檢視專案目錄中的 Compose 檔案:

$ ls -l
total 8
-rw-r--r-- 1.1k 5 Dec 17:45 compose.yaml
-rw-r--r-- 1.2K 5 Dec 17:54 gpu-compose.yaml

我們可以看到有兩個 Compose 檔案:標準版和支援 GPU 的版本。這是 Docker 靈活性的體現,能夠根據不同硬體環境提供對應的設定方案。

選擇適合的啟動方式

如果你的主機支援 GPU 與已啟用,可以使用 GPU 版本的 Compose 檔案來加速 AI 模型的運算:

$ docker compose --file gpu-compose.yaml up --detach

若不確定或沒有 GPU 支援,則使用標準版本:

$ docker compose up --detach

啟動過程說明

啟動過程中,Docker 會執行以下操作:

  1. 下載必要的容器映像檔
  2. Ollama(AI 模型執行時)會下載指定的 AI 模型
  3. 設定網路和儲存卷
  4. 啟動並連線所有服務

這個過程可能需要一些時間,特別是在網路連線較慢的情況下。這是因為 AI 模型通常較大,需要下載數 GB 的資料。

檢查應用狀態

啟動後,可以檢查應用程式的狀態:

$ docker compose ls
NAME    STATUS        CONFIG FILES
chatbot running(3)    ~/gsd-book/chatbot/compose.yaml

當 STATUS 欄位顯示三個執行中的服務時,表示聊天機器人已成功啟動並執行。接著,可以透過瀏覽器存取 http://localhost:3000 來使用聊天機器人,並開始提問簡單的問題。

深入 Ollama 容器:AI 模型伺服器

Ollama 是一個開放原始碼的 AI 模型執行時,它允許你在本地執行 AI 模型,確保所有互動保持私密。讓我們深入瞭解 Ollama 容器的設定:

$ docker exec -it chatbot-ollama-1 sh

進入容器後,可以檢查 Ollama 的版本:

# ollama --version
ollama version is 0.4.0-rc8

檢視已安裝的 AI 模型

# ollama ls
NAME           ID             SIZE     MODIFIED
mistral:latest f974a74358d6   4.1 GB   28 minutes ago
gemma2:latest  ff02c3702f32   5.4 GB   6 days ago

這個容器已經安裝了兩個模型:Mistral 和 Gemma2。這些都是開放原始碼的大語言模型,可以用於自然語言處理任務。

檢查執行中的程式

$ ps -ef
UID PID PPID C STIME TTY     TIME CMD
root 1   0   0 18:08 ?       00:00:00 /bin/bash /usr/loc
root 7   1   0 18:08 ?       00:00:00 /bin/ollama serve

可以看到 Ollama 服務正在執行。

檢視正在使用的模型

# ollama ps
NAME           ID             SIZE     PROCESSOR UNTIL
mistral:latest f974a74358d6   5.9 GB   100% CPU  4 minutes

這表明 Mistral 模型目前正在使用,並佔用了 100% 的 CPU 資源。這是典型的 AI 模型執行狀態,因為這些模型通常需要大量計算資源。

清理資源

當完成測試後,可以停止聊天機器人並清理所有資源:

$ docker compose down --volumes --rmi all
[+] Running 8/8
- Container chatbot-frontend-1 Removed
- Container chatbot-backend-1 Removed
- Container chatbot-ollama-1 Removed
- Image nigelpoulton/gsd-book:chat-frontend Removed
- Image nigelpoulton/gsd-book:chat-model Removed
- Volume ollama_data Removed
- Image nigelpoulton/gsd-book:chat-backend Removed
- Network chatbot_default Removed

這個命令會:

  1. 停止並移除所有容器
  2. 刪除所有下載的映像檔
  3. 移除所有建立的卷
  4. 清理網路設定

這是 Docker 的另一個強大特性 - 容易清理和重置環境,特別適合開發和測試場景。