Docker 的匯入簡化了應用程式佈署流程,讓開發團隊能更專注於程式碼而非伺服器組態。相較於 Chef 和 Puppet 等傳統組態管理工具,Docker 採用映像檔封裝應用程式及其依賴項,確保了環境一致性,也降低了佈署的複雜度。然而,生產環境的 Docker 應用仍需考量佈署策略、健康檢查、日誌收集、監控以及故障還原等導向。從手動建置映像檔到整合 CI/CD 系統自動化流程,選擇合適的策略至關重要。

Docker在生產環境中的應用與挑戰

Docker技術的出現,為軟體開發與佈署帶來了革命性的變化。相較於傳統的組態管理工具,如Chef和Puppet,Docker提供了一種更為簡便、一致的佈署方式,逐漸成為DevOps團隊的首選方案。本文將探討Docker在生產環境中的應用、優勢以及面臨的挑戰。

Docker與組態管理

傳統的組態管理工具主要用於自動化伺服器的組態和管理。然而,隨著Docker的興起,許多團隊開始轉向使用Docker來簡化組態管理的複雜性。在許多案例中,組態管理工具僅被用來為主機安裝Docker,而其他組態則交由Docker處理。

從開發到生產環境

本文主要關注Docker在生產環境中的應用,而非開發環境。雖然如此,瞭解Docker如何影回應用程式碼的佈署仍然十分重要。與傳統的組態管理系統不同,Docker的最佳實踐是將應用程式碼預先封裝到Docker映像檔中。映像檔通常包含所有應用程式碼、執行階段依賴項和系統需求。資料函式庫憑證和其他敏感資訊通常在執行階段新增到映像檔中,而不是在建置時。

手動建置與自動化建置

一些團隊選擇在開發機器上手動建置Docker映像檔,並將其推播到映像檔倉函式庫,然後從倉函式庫中提取映像檔到生產主機。這種方法雖然簡單,但由於工作流程和安全性的問題,並非理想選擇。更常見的做法是使用CI/CD系統,在應用程式碼或Dockerfile變更時自動建置新的映像檔。

Docker的多樣化應用

隨著技術的不斷演進,從實體伺服器到虛擬伺服器,再到雲端平台即服務(PaaS)環境,Docker映像檔可以在現有環境中使用,也可以與全新的架構結合。不需要立即將單體式應用程式遷移到服務導向架構,就能開始使用Docker。Docker有多種使用場景,例如:

  • 使用根據映像檔的佈署取代Capistrano等程式碼佈署系統。
  • 在同一台伺服器上安全地執行舊版和新版應用程式。
  • 使用一套工具逐步遷移到服務導向架構。
  • 管理雲端或裸機上的水平擴充套件性和彈性。
  • 確保從開發到測試,再到生產的多個環境之間的一致性。
  • 簡化開發者機器設定的複雜度。

逐步遷移至Docker

將應用程式的背景工作程式遷移到Docker叢集,同時保留Web伺服器和資料函式庫伺服器不變,是開始使用Docker的常見做法。另一個例子是將應用程式的REST API部分遷移到Docker中,並在前端使用Nginx代理來路由舊版和Docker叢集之間的流量。使用這些技術,團隊可以無縫地從單體式架構遷移到服務導向架構。

Docker的優勢

現代應用程式通常需要數十種第三方函式庫來加速功能開發或連線到第三方SaaS和資料函式庫服務。每個函式庫都可能引入錯誤或依賴版本管理的問題。加上頻繁的函式庫變更,所有這些都對基礎設施上的一致佈署工作程式碼造成了巨大的壓力。

Docker的「黃金映像檔」理念允許團隊以可測試、可重複、檔案化且一致的方式佈署工作程式碼,無論是單體式、服務導向還是混合架構,都可以透過將程式碼和依賴項封裝在同一個映像檔中實作。一旦映像檔建置完成,就可以佈署到任意數量的執行Docker守護程式的伺服器上。

生產環境中的挑戰

在生產環境中執行Docker容器雖然具有挑戰性,但已經有越來越多的公司開始採用。與所有基礎設施一樣,應從小處開始,逐步遷移。生產環境需要堅如磐石的佈署、健康檢查、最小或零停機時間、故障還原(回復)能力、集中儲存日誌、應用程式效能監控以及指標匯聚等功能。新技術如Docker固然有趣,但需要時間來完善。

為何Docker在生產環境中具有挑戰性?

大多數團隊採用Docker的原因包括:

  • 應用程式不同部分有許多不同的依賴項。
  • 需要支援具有舊依賴項的舊版應用程式。
  • 開發人員與DevOps團隊之間的工作流程問題。

接受訪談的團隊中,有一個共同的警示,即試圖在組織內一舉採用Docker可能存在風險。即使維運團隊已準備好採用Docker,轉向Docker往往意味著將管理依賴項的負擔推給開發人員。雖然許多開發人員渴望這種自主權,因為它允許他們更快地迭代,但並非每位開發人員都具備或願意承擔這一責任。遷移公司文化以支援良好的Docker工作流程需要時間。

# 使用官方Python映像檔作為基礎
FROM python:3.9-slim

# 設定工作目錄
WORKDIR /app

# 複製requirements.txt檔案到工作目錄
COPY requirements.txt .

# 安裝Python依賴項
RUN pip install --no-cache-dir -r requirements.txt

# 複製應用程式碼到工作目錄
COPY . .

# 暴露應用程式埠
EXPOSE 8000

# 設定執行命令
CMD ["python", "app.py"]

內容解密:

上述Dockerfile展示瞭如何為Python應用程式建立Docker映像檔。首先,使用官方的Python 3.9映像檔作為基礎,接著設定工作目錄並複製requirements.txt檔案到容器中。然後,安裝Python依賴項,並將應用程式碼複製到容器中。最後,暴露應用程式所需的埠,並設定容器啟動時執行的命令。這種做法確保了應用程式及其依賴項被封裝在一起,提高了佈署的一致性和可移植性。

結語

總而言之,Docker為軟體開發和佈署帶來了諸多益處,包括提高佈署的一致性、簡化依賴管理等。然而,在生產環境中採用Docker也面臨諸多挑戰,如需要堅如磐石的佈署流程、健康檢查和故障還原能力等。隨著技術的發展和實踐經驗的積累,越來越多的團隊能夠克服這些挑戰,充分發揮Docker的潛力。

Docker在生產環境中的流程圖示

  graph LR;
    A[開始] --> B[建置Docker映像檔];
    B --> C[推播映像檔到倉函式庫];
    C --> D[在生產環境中提取映像檔];
    D --> E[執行Docker容器];
    E --> F[健康檢查];
    F -->|正常| G[繼續執行];
    F -->|異常| H[故障處理];
    G --> I[日誌收集];
    H --> J[回復或修復];
    I --> K[指標匯聚];
    J --> E;
    K --> L[監控與報告];

圖表翻譯: 此圖示展示了Docker在生產環境中的典型流程。首先,從建置Docker映像檔開始,將其推播到倉函式庫。然後,在生產環境中提取映像檔並執行Docker容器。接著進行健康檢查,如果容器執行正常,則繼續執行並收集日誌;如果異常,則進行故障處理,如回復或修復。日誌收集後進行指標匯聚,最終用於監控與報告。這種流程確保了生產環境中的穩定性和可監控性。

隨著對Docker理解的加深和實踐經驗的積累,團隊能夠更好地利用Docker提高開發效率和生產環境的穩定性。在未來的發展中,持續最佳化和改進Docker的使用方式,將進一步釋放其潛力,為軟體開發和佈署帶來更多創新和突破。

Docker 生產環境的架構元件

在生產環境中使用 Docker 時,通常會包含幾個基本的架構元件。這些元件對於執行伺服器叢集(無論是容器化還是傳統的)都是通用的。建立和執行容器的過程與建立和執行虛擬機器的過程相似,但需要使用新的工具和技術。

基本步驟

  1. 建立和快照映像:首先,需要建立 Docker 映像並對其進行快照。
  2. 上傳映像到儲存函式庫:建立好的映像需要上傳到映像儲存函式庫中。
  3. 從儲存函式庫下載映像到主機:主機需要從映像儲存函式庫下載所需的映像。
  4. 將映像執行為容器:下載的映像需要在主機上執行為容器。
  5. 將容器連線到其他服務:執行的容器需要與其他服務進行連線。
  6. 將流量路由到容器:需要將外部流量路由到執行的容器中。
  7. 將容器日誌傳送到某處:容器的日誌需要被收集並傳送到特定的位置。
  8. 監控容器:需要對執行的容器進行監控,以確保其正常執行。

與虛擬機器相比,容器提供了更大的靈活性,因為它們將主機(裸機或虛擬機器)與應用程式服務分離。這使得建立和組態流程更加直觀,但也由於額外的巢狀層而帶來了一些額外的開銷。

典型的 Docker 技術堆疊

典型的 Docker 技術堆疊會包含以下元件,以解決不同的問題:

  • 建置系統:負責建立 Docker 映像並將其推播到映像儲存函式庫。
  • 映像儲存函式庫:用於儲存 Docker 映像。
  • 主機管理:負責組態和管理主機,包括安裝 Docker 守護程式。
  • 組態管理:定義容器叢集、處理執行時組態以及管理金鑰和秘密。
  • 佈署:將映像佈署到主機上。
  • 協調:負責組織容器叢集、排程伺服器資源以及路由流量到容器。
  • 日誌記錄:收集和儲存容器的日誌。
  • 監控:對容器進行監控,以確保其正常執行。

建置系統

建置系統負責建立 Docker 映像並將其推播到映像儲存函式庫。常見的做法有兩種:

  1. 手動建立:開發人員在本地手動建立 Docker 映像並推播到儲存函式庫。
  2. 自動建立:使用 CI/CD 系統(如 Jenkins 或 Codeship)在程式碼推播時自動建立映像。

理想的生產環境應該使用 CI/CD 系統來自動建立映像,並將其推播到映像儲存函式庫。

映像儲存函式庫

映像儲存函式庫用於儲存 Docker 映像。目前,Docker 官方的映像儲存函式庫 Hub 的可靠性仍有待提高,因此大多數團隊會選擇在自己的基礎設施上執行自己的映像儲存函式庫,以減少網路傳輸成本和延遲。

簡單實用的 Docker 環境範例

對於較小的團隊來說,使用 Docker 可以帶來很多好處,例如簡化建立和佈署服務的流程,並使其具有可重複性和可擴充套件性。Peerspace 就是這樣一個例子,他們採用了一種極簡主義的方法來建立 Docker 系統,從而實作了快速開發和穩定的生產環境。

簡化系統元件

Peerspace 的方法強調簡單性,每個系統元件(容器)都有一個明確的目標,並且以相同的方式執行相同的任務。這使得開發人員可以平行且獨立地開發不同的系統部分,而不必擔心容器之間的相容性問題。

組態管理

Peerspace 盡量避免使用傳統的組態管理工具,而是使用 Ansible、SaltStack、Chef 或 Puppet 等工具僅用於組態主機上的 Docker 守護程式。他們盡量減少對舊有組態管理系統的依賴,並轉向使用自組態的容器,利用本文中介紹的發現和叢集技術。

程式碼範例

以下是一個簡單的 Dockerfile 範例,用於建立一個 Nginx 伺服器:

# 使用官方 Nginx 映像作為基礎
FROM nginx:latest

# 複製自定義組態檔案到容器中
COPY nginx.conf /etc/nginx/nginx.conf

# 暴露 Nginx 的預設埠
EXPOSE 80

# 執行 Nginx
CMD ["nginx", "-g", "daemon off;"]

內容解密:

此 Dockerfile 使用官方的 Nginx 映像作為基礎,並將自定義的 nginx.conf 檔案複製到容器中的 /etc/nginx/nginx.conf。接著,它暴露了 Nginx 的預設埠 80,並設定了容器啟動時執行的命令為 nginx -g "daemon off;",以確保 Nginx 以前台模式執行。

圖表示例

以下是一個簡單的 Mermaid 圖表,用於展示 Docker 容器的生命週期:

  graph LR;
    A[建立映像] --> B[上傳映像到儲存函式庫];
    B --> C[從儲存函式庫下載映像到主機];
    C --> D[執行映像為容器];
    D --> E[將容器連線到其他服務];
    E --> F[將流量路由到容器];
    F --> G[收集容器日誌];
    G --> H[監控容器];

圖表翻譯: 此圖表展示了 Docker 容器的生命週期,從建立映像到監控容器的整個過程。每個步驟都代表了 Docker 容器在生產環境中的一個關鍵階段。

簡化系統架構:PeerSpace 的實務經驗

在現代軟體開發與佈署的過程中,保持系統的簡單性是一項極具挑戰性的任務。PeerSpace 作為一個成功的範例,透過一系列設計原則與實踐,成功地簡化了其生產環境。本文將探討 PeerSpace 如何透過技術選型和系統設計來實作這一目標。

系統設計原則

PeerSpace 的系統由約 20 個微服務組成,部分服務由 MongoDB 資料函式庫和 ElasticSearch 搜尋引擎支援。為了保持系統的簡單性,PeerSpace 遵循以下設計原則:

  1. 偏好無狀態服務:大多數服務被設計為無狀態,這意味著它們不會儲存任何需要持久化的資訊,除了處理當前請求所需的臨時資訊。這種設計使得服務容易被銷毀、重啟、複製和擴充套件,而無需考慮資料處理的問題。

    # 無狀態服務範例
    from flask import Flask, request
    
    app = Flask(__name__)
    
    @app.route('/process', methods=['POST'])
    def process_request():
        data = request.get_json()
        # 處理請求資料
        result = process_data(data)
        return result
    
    def process_data(data):
        # 模擬資料處理過程
        return {'status': 'success', 'data': data}
    

    內容解密:

    • 上述程式碼展示了一個簡單的無狀態服務,使用 Flask 框架建立了一個處理 POST 請求的端點。
    • process_request 函式接收 JSON 資料並呼叫 process_data 進行處理,最後傳回結果。
    • 這種無狀態設計使得服務可以輕易地擴充套件和重啟,而不必擔心資料的一致性問題。
  2. 偏好靜態組態:所有主機和服務的組態都是靜態的,一旦組態被推播到伺服器,除非明確推播新的組態,否則組態保持不變。靜態組態使得系統更容易理解和故障排除。

    # 靜態組態範例
    services:
      - name: web-service
        image: web-service:latest
        ports:
          - "80:80"
        environment:
          - DATABASE_URL=mongodb://db-host:27017
    

    內容解密:

    • 上述 YAML 組態檔案定義了一個名為 web-service 的服務,使用 web-service:latest 映象,並將主機的 80 連線埠對映到容器的 80 連線埠。
    • 環境變數 DATABASE_URL 被設定為 mongodb://db-host:27017,指定了資料函式庫的位置。
    • 這種靜態組態方式使得服務的佈署和管理變得簡單直接。
  3. 網路佈局也是靜態的:服務的位置是固定的,除非進行新的組態,否則不會改變。這種設計減少了因服務位置變更而導致的複雜性。

  4. 區別對待無狀態和有狀態服務:PeerSpace 認為無狀態服務(如一般的微服務)和有狀態服務(如資料函式庫)應當區別對待。無狀態服務可以輕易地在不同主機之間遷移,而有狀態服務則需要考慮資料遷移的問題。

簡化維運流程

PeerSpace 的維運流程設計根據一個觀察:基礎設施中越接近硬體的層次變化越少,而越接近使用者的層次變化越多。因此,他們將最頻繁的變更操作設計得盡可能簡單。

  1. 伺服器數量變化少:生產環境中的伺服器數量很少變化,通常是由於擴充套件或硬體故障。
  2. 服務變更更頻繁:執行在伺服器上的服務種類別和數量變化較多,包括新增服務、更新版本或重新組態。
  3. 頻繁的版本佈署:PeerSpace 每天會進行多次服務版本的佈署,大多數情況下只是替換目前的服務為新版本。

系統詳細架構

PeerSpace 執行著三個類別似生產環境的叢集:整合、預發布和生產。每個叢集包含相同數量的服務,並且組態相似,只是處理能力不同。

  1. Docker 主機:執行 CentOS 7,使用 systemd 作為系統監控器。
  2. MongoDB 和 ElasticSearch:用於資料儲存和搜尋,有些環境中這些服務被 Docker 化,有些則不是。在生產環境中,出於效能和維運考慮,這些資料服務未被 Docker 化。

每個 Docker 主機執行一套靜態的服務,每個服務都遵循相同的模式:

  • 所有組態透過環境變數設定。
  • 不寫入任何資料到磁碟。
  • 將日誌輸出到 stdout。
  • 生命週期由 systemd 管理。
  graph LR
    A[Docker 主機] --> B[Service 1]
    A --> C[Service 2]
    A --> D[MongoDB]
    A --> E[ElasticSearch]
    B --> F[stdout 日誌]
    C --> F
    D --> G[資料儲存]
    E --> H[搜尋引擎]

圖表翻譯: 此圖示展示了一個 Docker 主機上執行的各種服務及其相互關係。Docker 主機上執行著多個服務(如 Service 1 和 Service 2),以及資料函式庫和搜尋引擎。所有服務的日誌都被輸出到 stdout,而資料函式庫和搜尋引擎則負責資料儲存和搜尋功能。

內容解密:

  • 該 Mermaid 圖表清晰地展示了 PeerSpace 系統架構中的主要元件及其互動關係。
  • Docker 主機是核心,上面執行著多個微服務以及必要的資料函式庫和搜尋引擎。
  • 每個元件都有其特定的功能,並且透過特定的方式(如 stdout 輸出日誌)進行互動。