Docker 的普及改變了應用程式佈署的格局,容器化技術讓應用程式及其依賴項封裝成單一可移植單元,簡化了佈署流程並提升了可移植性。本文從 Dockerfile 的撰寫開始,逐步講解如何建置、執行 Docker 容器,並使用 Docker Compose 管理多個容器。同時,文章也分析了組態管理工具的發展歷程,並闡述了 Ansible 在 Docker 生態系統中的優勢,最後以實際案例示範如何使用 Ansible 組態生產環境。隨著微服務架構的盛行,容器化技術和自動化組態管理已成為現代軟體開發不可或缺的一部分,本文旨在提供一個實用的,幫助開發者和維運人員更好地掌握 Docker 佈署流程和 Ansible 的組態管理技巧。
佈署流程的實作:初始階段
在前面的章節中,我們已經討論了Dockerfile的基本結構和指令。現在,讓我們探討如何使用Dockerfile來建立我們的容器。
Dockerfile解析
首先,讓我們逐一分析Dockerfile中的每一行指令。
COPY run.sh /run.sh
RUN chmod +x /run.sh
COPY target/scala-2.10/books-ms-assembly-1.0.jar /bs.jar
COPY client/components /client/components
內容解密:
COPY指令:將主機檔案系統中的檔案複製到正在建立的容器中。格式為
COPY <source>... <destination>。來源路徑相對於Dockerfile的位置,且必須在建置環境的脈絡中。COPY run.sh /run.sh:將run.sh指令碼檔案複製到容器的根目錄。COPY target/scala-2.10/books-ms-assembly-1.0.jar /bs.jar:將後端JAR檔案複製到容器的根目錄並重新命名為bs.jar。COPY client/components /client/components:將前端元件複製到容器的/client/components目錄。
RUN指令:在容器中執行命令。在這裡,
RUN chmod +x /run.sh用於使run.sh指令碼具有可執行許可權。
接下來,讓我們看看Dockerfile的最後兩行指令。
CMD ["/run.sh"]
EXPOSE 8080
內容解密:
- CMD指令:指定容器啟動時要執行的命令。格式為
["executable", "parameter1", "parameter2"...]。在我們的例子中,/run.sh將在沒有任何引數的情況下執行。 - EXPOSE指令:指定容器內部哪些埠號在執行時可用。在我們的例子中,容器內的8080埠號將被暴露。
建置容器
有了對Dockerfile的深入瞭解後,讓我們來建置容器。命令如下:
docker build -t 10.100.198.200:5000/books-ms .
內容解密:
docker build:用於建置容器的命令。-t:允許我們為容器加上特定的標籤。在這裡,我們使用了私有登入檔的IP和埠號,而不是Docker Hub的使用者名稱。.:指定Dockerfile位於當前目錄。
Dockerfile指令的順序
Dockerfile中的指令順序非常重要。一方面,它需要是邏輯性的;另一方面,我們需要考慮Docker的快取機制。當執行docker build命令時,Docker會逐一檢查指令是否已經建立了映像檔。一旦發現需要建立新映像檔的指令,Docker就會建置該指令及其後的所有指令。因此,COPY和ADD指令通常應該放在Dockerfile的底部。
執行容器
一旦容器建置完成,我們就可以執行它。執行我們剛剛建置的容器的命令如下:
docker run -p 8080:8080 10.100.198.200:5000/books-ms
內容解密:
(此處假設使用預設的執行命令,未直接提供於原文中)
docker run:用於執行容器的命令。-p 8080:8080:將主機的8080埠號對映到容器的8080埠號。
這樣,我們就成功地使用Dockerfile建立並執行了我們的容器。接下來的章節中,我們將繼續探討更多關於Docker的使用和佈署流程的最佳實踐。
佈署流程的實作:初始階段的容器管理與 Docker Compose
在前述章節中,我們探討瞭如何使用 Docker 容器化技術來佈署我們的微服務。本章節將深入介紹如何使用 Docker 命令列工具以及 Docker Compose 來簡化佈署流程。
使用 Docker 命令列工具佈署容器
首先,我們使用 docker run 命令來啟動兩個容器:一個執行 MongoDB 資料函式庫,另一個執行我們的微服務 books-ms。
docker run -d --name books-db mongo
docker run -d --name books-ms \
-p 8080:8080 \
--link books-db:db \
10.100.198.200:5000/books-ms
內容解密:
- 第一條命令:啟動一個名為
books-db的容器,使用官方的 MongoDB 映像。-d引數使容器在背景執行。 - 第二條命令:啟動一個名為
books-ms的容器,將主機的 8080 連線埠對映到容器的 8080 連線埠,並將books-ms容器連結到books-db容器,使用別名db。 - 連結容器的作用是讓
books-ms容器可以透過環境變數存取books-db容器。
為了驗證連結是否成功建立,我們可以進入 books-ms 容器並檢查環境變數。
docker exec -it books-ms bash
env | grep DB
exit
內容解密:
- 進入容器:使用
docker exec命令以互動模式進入books-ms容器,並執行bash。 - 檢查環境變數:使用
env命令列出所有環境變數,並透過grep過濾出包含DB的變數。 - 結果顯示了由 Docker 自動產生的環境變數,例如
DB_PORT_27017_TCP和DB_ENV_MONGO_VERSION,這些變數幫助books-ms容器與books-db容器進行通訊。
使用 Docker Compose 簡化佈署
儘管使用 docker run 命令可以完成佈署,但引數繁多且容易出錯。因此,我們引入 Docker Compose 來簡化這一過程。
首先,移除之前啟動的容器。
docker rm -f books-ms books-db
然後,使用 Docker Compose 啟動容器。
docker-compose -f docker-compose-dev.yml up -d app
內容解密:
docker-compose命令:使用-f引數指定組態檔案docker-compose-dev.yml,並使用up命令啟動服務。-d引數:使服務在背景執行。app指定服務:只啟動app服務及其依賴的服務。
檢視 docker-compose-dev.yml 檔案的內容,可以看到對應的服務定義。
app:
image: 10.100.198.200:5000/books-ms
ports:
- "8080:8080"
links:
- db:db
db:
image: mongo
內容解密:
app服務:使用指定的映像,對映連線埠,並連結到db服務。db服務:使用官方的 MongoDB 映像。
透過 Docker Compose,我們可以用簡單的命令完成複雜的容器佈署和管理,大大簡化了開發和佈署流程。
佈署流程的實作:初始階段
在前面的章節中,我們已經建立了一個基本的開發流程,接下來,我們將探討佈署流程的實作。在這個過程中,我們將使用 Docker Compose 來管理和執行多個容器。
使用 Docker Compose 管理容器
Docker Compose 是一個強大的工具,可以用來定義和執行多個 Docker 容器。在我們的例子中,我們使用它來啟動 books-ms 服務和其依賴的 MongoDB 容器。
docker-compose ps
執行上述命令後,我們可以看到類別似以下的輸出:
Name Command State Ports
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
booksms_app_1 /run.sh Up 0.0.0.0:8080->8080/tcp
booksms_db_1 /entrypoint.sh mongod Up 27017/tcp
內容解密:
docker-compose ps命令用於列出由 Docker Compose 管理的正在執行的容器。- 輸出結果顯示了容器的名稱、執行的命令、狀態以及埠對映情況。
booksms_app_1是我們的books-ms服務容器,它將本地的 8080 埠對映到了容器的 8080 埠。booksms_db_1是 MongoDB 容器,負責提供資料函式庫服務。
手動驗證服務功能
為了驗證我們的服務是否正常工作,我們可以使用 curl 命令來傳送 HTTP 請求。
curl -H 'Content-Type: application/json' -X PUT -d \
'{"_id": 1, "title": "My First Book", "author": "John Doe", "description": "Not a very good book"}' \
http://localhost:8080/api/v1/books | jq '.'
內容解密:
curl是一個用於傳送 HTTP 請求的命令列工具。-H 'Content-Type: application/json'指定了請求的內容型別為 JSON。-X PUT指定了請求的方法為 PUT,用於更新或建立資源。-d後面的字串是請求的主體內容,包含了書籍的詳細資訊。http://localhost:8080/api/v1/books是請求的 URL,指向我們的books-ms服務。jq '.'用於格式化輸出的 JSON 資料,使其更易讀。
將容器推播到 Registry
在驗證了服務的功能後,我們需要將容器推播到 Docker Registry,以便在其他伺服器上提取和使用。
docker push 10.100.198.200:5000/books-ms
內容解密:
docker push命令用於將本地的 Docker 容器推播到遠端的 Registry。10.100.198.200:5000/books-ms是容器的完整名稱,包括了 Registry 的地址和埠,以及容器的名稱。
銷毀虛擬機器
在完成本章節的任務後,我們將銷毀所有的虛擬機器,以便在下一章節中重新建立所需的環境。
vagrant destroy -f
內容解密:
vagrant destroy命令用於銷毀由 Vagrant 管理的虛擬機器。-f引數強制銷毀虛擬機器,無需確認。
Docker 世界中的組態管理
管理多台伺服器時,手動執行任務不僅耗時且風險高。組態管理(CM)已經存在很長時間,沒有理由不採用其中一種工具。問題不在於是否採用某種工具,而在於選擇哪一種。那些已經採用某種工具並投入大量時間和金錢的人可能會認為,最好的工具就是他們所選擇的那一個。
隨著時間的推移,人們的選擇會發生變化,今天選擇某種工具的原因可能與昨天不同。在大多數情況下,決策並非根據可用的選項,而是根據我們所維護的舊系統架構。如果忽視這些系統,或者有人有足夠的勇氣和財力願意對其進行現代化,那麼今天的現實將由容器和微服務主導。在這種情況下,我們昨天的選擇與今天可以做出的選擇不同。
CFEngine:組態管理的始祖
CFEngine⁵⁷ 可以被視為組態管理的始祖。它建立於 1993 年,徹底改變了我們處理伺服器設定和組態的方式。它最初是一個開源專案,2008 年發布了第一個企業版本後開始商業化。
CFEngine 使用 C 語言編寫,依賴性少,速度極快。據我所知,沒有其他工具能夠超越 CFEngine 的速度。這是它主要的優勢。然而,它也有其弱點,其中最主要的是需要具備編碼技能。在許多情況下,一般的操作員無法充分利用 CFEngine。它需要 C 語言開發人員進行管理。這並沒有阻止它在一些最大的企業中被廣泛採用。然而,正如年輕通常勝過年老,新工具不斷被創造出來,今天很少有人會選擇 CFEngine,除非公司已經對其進行了大量投資。
內容解密:
CFEngine 的優點在於其高效能和快速的組態管理能力,但其需要 C 語言技能的門檻限制了其普及率。
Puppet:更友好的組態管理工具
後來,Puppet⁵⁸ 出現了。它也從開源專案開始,隨後推出了企業版本。由於其模型驅動的方法和相對較小的學習曲線,被認為更「維運友好」。終於有一種組態管理工具能夠被維運部門所利用。與 CFEngine 使用的 C 語言不同,Ruby 語言更容易理解,也更容易被維運人員接受。CFEngine 的學習曲線可能是 Puppet 在組態管理市場上站穩腳跟的主要原因,並慢慢地將 CFEngine 送入歷史。並不是說 CFEngine 不再被使用,它仍然被使用,而且似乎不會很快消失,就像 Cobol 仍然存在於許多銀行和其他金融相關業務中一樣。然而,它已經失去了作為首選工具的聲譽。
內容解密:
Puppet 提供了更友好的組態管理方式,降低了使用門檻,使其成為維運部門的首選工具之一。
Chef:另一種組態管理選擇
接著,Chef⁵⁹ 出現了,承諾解決 Puppet 的一些問題。一段時間內,它確實做到了這一點。後來,隨著 Puppet 和 Chef 的流行度不斷增加,它們進入了「零和遊戲」。一旦其中一個推出了新的功能或改進,另一個就會跟進。兩者都具有越來越多的工具,這往往會增加它們的學習曲線和複雜性。Chef 對開發者更友好,而 Puppet 則更傾向於維運和系統管理型別的任務。兩者之間沒有明顯的優勢,選擇往往根據個人經驗而非其他因素。Puppet 和 Chef 都很成熟,在企業環境中被廣泛採用,並且有大量的開源貢獻。唯一的缺點是,它們對於我們想要實作的目標來說太過複雜。它們都不是為容器設計的,也無法預見 Docker 的出現會改變「遊戲規則」。
內容解密:
Chef 和 Puppet 之間的競爭推動了兩者的發展,但它們都沒有針對容器的特點進行最佳化。
Docker 世界中的組態管理需求
到目前為止,我們提到的所有組態管理工具都在試圖解決一些問題,而這些問題在我們採用容器和不可變佈署後本來是不應該存在的。以前存在的伺服器混亂局面已經不復存在。現在,我們需要處理的是大量的容器和其他非常有限的資源。這並不意味著我們不需要組態管理,我們仍然需要!然而,所選工具的作用範圍要小得多。在大多數情況下,我們只需要一兩個使用者,Docker 服務執行正常,還有一些其他東西。其他一切都是容器。佈署正成為另一套工具的主題,並重新定義了組態管理的作用範圍。Docker Compose、Mesos、Kubernetes 和 Docker Swarm,只是我們今天可能使用的迅速增加的佈署工具中的幾個。在這種情況下,我們的組態管理選擇應該優先考慮簡單性和不可變性,而不是其他因素。語法應該簡單易讀,即使對於從未使用過該工具的人來說也是如此。不可變性可以透過強制推播模型來實作,該模型不需要在目標伺服器上安裝任何東西。
Ansible:根據 SSH 的組態管理
Ansible⁶⁰ 試圖以非常不同的方式解決與其他組態管理工具相同的問題。一個顯著的不同之處在於,它透過 SSH 執行所有操作。與其他工具相比,這是一種更簡單、更直接的方法。
內容解密:
Ansible 利用 SSH 進行組態管理,提供了一種簡潔高效的方式來管理和佈署容器化應用。
組態管理在Docker世界中的選擇與實踐
在眾多的組態管理(Configuration Management, CM)工具中,Ansible因其簡單性和與現有系統的相容性而脫穎而出。與Chef、Puppet和CFEngine等競爭對手相比,Ansible不需要在被管理的伺服器上安裝客戶端,而是利用SSH(幾乎總是存在)來執行必要的命令以確保目標伺服器符合我們的規範。
Ansible的優勢
- 無需客戶端安裝:Ansible透過SSH協定執行命令,不需要在被管理的伺服器上安裝任何客戶端軟體。
- 依賴現有設施:Ansible利用了Linux系統中預裝的Python,使得其佈署更加方便。
- 簡單易學:Ansible的語法根據YAML,易於理解和學習。即使是沒有使用過Ansible的人,也可以透過閱讀playbook快速理解其運作邏輯。
- 開發者友善:與其他組態管理工具相比,Ansible更注重實用性和易用性,而不是強迫使用者學習複雜的語言或DSL。
Ansible在Windows上的侷限性
有人可能會指出,Ansible對Windows的支援有限是一個缺點。然而,在容器化(containerization)的背景下,這一侷限性反而成為了一個優勢。因為Docker目前尚未完全支援在Windows上執行容器,這使得Ansible專注於Linux上的命令執行變得更加合理。
組態管理的未來與選擇
隨著容器技術(如Docker)的發展和不可變佈署(immutable deployments)的興起,組態管理的選擇變得更加明確。Ansible結合Docker及其佈署工具,成為了一個強有力的組合。即使有人認為組態管理工具在未來可能會被容器和佈署工具所取代,但就目前而言,Ansible仍然是一個非常有價值的工具。
實際操作:使用Ansible組態生產環境
讓我們來看看如何使用Ansible來組態生產環境。首先,我們需要啟動兩個虛擬機器(VMs):cd和prod。cd將作為我們設定prod節點的伺服器。
vagrant up cd prod --provision
vagrant ssh cd
ansible-playbook /vagrant/ansible/prod.yml -i /vagrant/ansible/hosts/prod
內容解密:
vagrant up cd prod --provision:此命令啟動並組態cd和prod虛擬機器。vagrant ssh cd:透過SSH登入到cd虛擬機器。ansible-playbook /vagrant/ansible/prod.yml -i /vagrant/ansible/hosts/prod:執行Ansible playbook來組態prod節點。-i引數指定了inventory檔案的位置。
執行的輸出結果將類別似於以下內容:
PLAY [prod] *******************************************************************
GATHERING FACTS ***************************************************************
The authenticity of host '10.100.198.201 (10.100.198.201)' can't be established.
ECDSA key fingerprint is 2c:05:06:9f:a1:53:2a:82:2a:ff:93:24:d0:94:f8:82.
Are you sure you want to continue connecting (yes/no)? yes
ok: [10.100.198.201]
TASK: [common | JQ is present] ************************************************
changed: [10.100.198.201]
內容解密:
- Ansible首先收集目標主機的事實資訊,並確認連線的真實性。
- 執行了一個任務來確保JQ(一個輕量級的JSON處理工具)在目標主機上已經安裝。
結語
總之,Ansible因其簡單、易學和與現有系統的相容性,在眾多的組態管理工具中脫穎而出。結合Docker和不可變佈署,Ansible提供了一個強大的組態管理解決方案。無論是對於已經在使用容器技術的團隊,還是在探索組態管理自動化的團隊,Ansible都是值得考慮的選擇。