使用Spring Boot和Docker構建微服務架構

2021-02-21 分布式實驗室


本文是《使用Spring Boot和Docker構建微服務架構》系列四部曲的前兩篇,對微服務架構以及容器化概念作一個概述,利用工具進行設置,深入探討如何使用Docker工作,然後搭建我們的第一個容器。

隨著越來越多的產品利用可重用的基於Rest的服務構建,人們很快發現把業務功能拆分成為可重用的服務是非常有效的,但同時也帶來了一個風險。每次更新服務,您必須重新測試與它一起部署的每一個服務,即使您有信心認為代碼更改不會影響其他服務,您永遠無法真正確保這一點,因為這些服務會不可避免地共享代碼、數據和其他組件。

隨著容器化的興起,你可以在一個完全隔離的環境中非常高效地運行代碼,把它們兩個組合在一起將會允許在細粒度可擴展性和版本控制方面的產品架構優化,不過付出的代價是增加了複雜性和一些重複的代碼。

答案是不完全是。容器和虛擬機具有一些相似性,它們都是通過一個控制進程管理的隔離環境(分別是容器管理器和虛擬機監控程序),但兩者之間的主要區別是,對於每一個虛擬機,運行的是一個完整的組件堆棧——從作業系統到應用伺服器,以及仿真的虛擬硬體包括網絡組件、CPU和內存。

對於容器來講,它們運行在更為完全隔離的沙盒中,出現在每個容器裡的僅僅是作業系統的最小內核,共享了底層系統的資源。容器化的最大優勢在於對於相同的硬體佔用空間更小,可以比虛擬機運行更多的實例。容器也有一些關鍵的限制:最大的一個是容器只能運行在基於Linux的作業系統上面(內核隔離是Linux的特定技術)。

與這一限制相關的就是Docker——目前最流行的容器服務提供系統——不能直接運行在Mac或者Windows系統上,因為它們不是Linux,替代方案就是為了運行Docker,你需要使用VirtualBox啟動一個Linux虛擬機,接著在虛擬機裡運行Docker。幸運的是,它絕大多數是由Docker ToolBox來管理的(原名Boot2Docker)。

Docker已經獲得了眾多的支持,以至於容器鏡像的公共Repository——Docker Hub,擁有超過了136,000個公共鏡像。其中許多是由個人創建的,一些擴展自「官方」鏡像然後按照他們自己的需求做了定製,但是其它的都是從「基礎」鏡像做了完整的平臺配置定製化。我們將利用這些「基礎」和「官方」鏡像開始我們的研究之旅。

「 所以我們已經談論了微服務和容器化,但是Spring Boot在哪個部分起作用呢?

我選擇使用Java來構建我自己的微服務,具體地說就是Spring Boot框架。選擇它主要是因為我熟悉Spring、易於開發的Rest服務Controller、業務服務以及數據存儲,還可以很容易地引入Scala的Akka/Play編程模型。微服務架構的其中一個最為人稱道的優勢就是服務的完全獨立,所以不需要也不應該關心每一個服務採用什麼語言或平臺構建。

就我個人而言,我認為多語言的維護成本要比獲得的彈性收益更大,但也有適用的用例,比如在一個大組織內的一個部門已經選擇了不同的技術棧為「標準」的情況下。另外一個可能的場景就是如果你決定從一個語言/平臺轉換到另外一個——你可以每次遷移一個微服務,提供相同的終端Web服務接口。我們努力的目標如下:

一個從開始設置微服務和Docker到如何結束的指南。

了解圍繞微服務架構的諸多組件的作出的不同決定的利弊——從原始碼控制到服務版本化,以及其中的一切。

分析「純粹」的微服務信仰,並且看它們如何應用到一個「現實世界」的場景。

看Docker是如何面對喧囂,以及為專業開發運行Docker什麼是必需的。

利用一系列的微服務構建一個完整的解決方案,每一個微服務都有它自己的容器,一個在自身容器託管的持久化層,還有容器集群。

其它有價值的內容。

我將模擬的業務場景是一家軟體開發公司的員工任務分配和識別系統,包含了以下任務:

我是在Mac上工作的,但是工具在Mac和PC上是相同的,所以在平臺上的操作是99%相同的,我將不會回顧如何安裝這些工具,而是直接從如何使用它們開始,你所需要的準備工作如下:

Docker Toolbox:包含了VirtualBox(作用為創建虛擬機用來運行容器)、Docker Machine(在虛擬機內運行Docker容器)、Docker Kitematic(一個管理在Docker Machine裡運行的容器的GUI)以及Docker Compose(多容器編排工具)

Git:我的GitHub連結在這兒(https://github.com/ThreePillarGlobal/microservice-blog),我在Windows上使用Git Extensions,在Mac上使用Source Tree,不過使用其他Git客戶端包括命令行都是可以的。

Java 8 SDK:Java 8在JVM永久代(PermGen)方面有了改進,另外對於Streams API和Lambda的支持也是非常贊的。

構建工具的選擇:讓我們使用Gradle吧,我推薦使用SDKMan,正式名稱為GVM來管理Gradle版本,如果你使用Windows工作,你可以使用Cygwin安裝SDKMan或者SDKMan的Powershell命令行工具,或者Gravy也是可替代的選擇。

IDE的選擇:我們使用Spring Tool Suite(STS)來工作,在本文寫作時最新的for Mac版本為3.7.0。

Rest工具:對於任何Web服務項目使用都非常方便,我是Chrome擴展工具Postman的粉絲,如果你擅長curl命令行,這個工具也是不錯的。

uMongo或者Mongo GUI:文檔型資料庫完美匹配自足性的模型——對象進行自動檢索,並且參考對象可以在微服務架構中通過ID來引用,這些ID可以很好地映射到文檔存儲中,另外地,MongoDB擁有運行很棒的「官方」Docker鏡像。

我們對於原始碼管理的第一個意見——似乎也是絕大多數的在線觀點,就是每個微服務都應該有自己的版本庫。微服務的一個基本理念就是跨服務之間不能共享代碼。就我個人而言,這對我架構師的小心臟是個小小的打擊,因為任何實用工具的重複代碼的數量可能會比較多,以及缺乏一個單一、統一的領域模型給我有點心痛的感覺。我理解這個原則——自足性意味著自力更生。為了這篇博文,我把所有的代碼都放到了一個單獨的代碼庫,然而,每個微服務在根目錄下都有它一個自己的目錄。這樣做是為了讓我可以隨著時間的推移展示分支的進展。在一個真實的解決方案中,你應該讓每一個微服務都有它自己的不同的版本庫,也許會有統一的版本庫引用其它的子模塊。

因為我們要處理隔離的、可重用的組件,我們將做以下的映射:
一個邏輯業務對象→一個微服務→一個Git版本庫目錄→一個Mongo集合
因為業務對象可能由多個對象組成,任何我們認為可以作為其自身業務對象的子對象都可以劃分為其自身的組件棧。

「 更多有關Docker如何工作的信息,以及我們的第一個容器

為了理解如何構建一個完整的基於Docker容器的產品解決方案,我們將需要深入研究容器是如何在宿主機(或者是虛擬機,正如我們的例子)裡運行的,使用Docker通常是有三個階段:鏡像構建、鏡像分發和容器部署。

構建鏡像——Dockerfile的世界

為了構建鏡像,你要寫一系列的指令用來獲取已有的鏡像,接著對該鏡像做些改變和配置。官方的Docker Hub Repository包含了許多的「官方」鏡像以及數以千計的用戶定製的鏡像。如果其中的鏡像不太符合你的需求,你可以創建一個定製的Dockerfile,用來在鏡像上逐步添加一些內容,,比如安裝系統軟體包、複製文件或者公開一些網絡埠,當我們構建微服務時,我們將會創建一個定製的Dockerfile,不過現在,我們將會利用一個標準鏡像來啟動一個MongoDB實例。

容器聯網

當容器啟動時,有一個它私有的網絡,容器宿主機埠將外部網絡通信轉發到單個的容器埠上,特定的容器埠可以通過Dockerfile來決定公開,並且埠轉發可以通過以下兩種方式之一來進行:你可以顯式地從宿主機映射埠到容器上,或者如果沒有顯式映射的話,Docker將映射已聲明的容器埠到宿主機一個可用的臨時埠上(通常的範圍是從32768到61000)。當我們可以對整個解決方案顯式地管理埠映射時,通常讓Docker處理這一切是一個更加好的主意,並且可以通過Link機制公開埠信息到容器上,這方面將在我們構建我們的第一個微服務容器時有所涉及。

啟動MongoDB容器

無論你是使用Kitematic還是Docker命令行,都可以非常簡單的啟動一個標準的容器。使用命令行的話,如果一切都安裝正確,命令提示符將會包含以下三個關鍵的環境變量:

DOCKER_HOST=tcp://192.168.99.100:2376DOCKER_MACHINE_NAME=defaultDOCKER_TLS_VERIFY=1DOCKER_CERT_PATH=/Users/[username]/.docker/machine/machines/default

這些是按照你的情況設置的(如果是在安裝過程中打開的話,你可能需要重啟終端或者命令提示符)。這些都是必要的,因為Docker不能直接運行在我的Mac筆記本上,而是跑在筆記本運行的虛擬機上。Docker客戶端非常有效地將Docker命令從我的筆記本「代理」到虛擬機上,在我們啟動容器前,讓我們重溫一下一部分Docker命令是非常有益的,在使用任何圖形用戶界面之前,了解命令行的東西總是很好的。

Docker級別的命令:

docker ps
該命令將會列出所有運行的容器,顯示的信息包括它們的ID、名字、基礎鏡像名字和埠映射信息等。

docker build
該命令用來定義一個鏡像——通過處理Dockerfile來創建一個新的鏡像,我們將用這個命令來構建我們的微服務鏡像。

docker pull[鏡像名字]
該命令從遠程Repository拉取鏡像並且存儲在本地。

docker run
該命令將基於一個本地或者遠程Repository(比如Docker Hub)啟動一個容器,我們將會相當多地探究這個命令。

docker push
該命令推送一個構建好的鏡像到一個Repository,通常是Docker Hub。

容器特定的命令

這些命令使用容器ID或者名字作為一個參數:

docker status [容器名字/ID][容器名字/ID]
這個命令將顯示指定的每一個容器的當前負載,比如CPU佔用率、內存使用率以及網絡流量等。

docker logs [-f][容器名字/ID]
該命令顯示容器的最新的日誌,-f選項就好比Shell終端中的「tail -f」中的-f選項。

docker inspect [容器名字/ID]
該命令將容器的所有配置信息以JSON的格式轉儲出來顯示。

docker port [容器名字/ID]
該命令顯示容器與宿主機之間的所有埠映射信息。

docker exec [-i][-t][容器名字/ID]
該命令將在目標容器上執行一條命令(-i表明以交互方式運行,-t表明以偽TTY終端運行),這個命令常用來獲得一個容器終端Shell:
docker exec -it [容器名字/ID]sh

一旦我們理解了這些參考材料,我們可以進入到下一步啟動一個Mongo容器。

命令非常簡單:docker run -P -d —name mongo mongo

解釋如下:

P選項告知Docker在臨時埠範圍裡公開容器聲明的任何埠

d選項告知以Daemon方式運行容器(比如在後臺)

name選項給容器分配一個名字(名字必須在所有運行的容器實例中唯一,如果你不提供這個選項,將會獲得一個隨機的半友好的名字比如modest_poitras)

最後的mongo表明了使用哪一個鏡像

Docker Hub鏡像的定義採用了[所有者]/[鏡像名字][:標籤]的命名格式,如果沒有指定所有者,那麼使用的就是「官方」的Docker Hub鏡像——這是預留給Docker官方給軟體供應商的禮物也就是成為官方鏡像,如果最後的標籤部分省略的話,那麼就會認為你需要獲得的是最新版本的鏡像。

現在我們來嘗試確認我們的Mongo實例已經啟動並且運行了:

docker exec -it mongo sh

mongo

MongoDB shell version: 3.0.6connecting to: testServer has startup warnings:2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten]2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten] ** We suggest setting it to 'never'2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten]2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten] ** We suggest setting it to 'never'2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten]> use microserviceblogswitched to db microserviceblog> db.createCollection('testCollection'){ "ok" : 1 }

在容器內部,Mongo看起來正在運行,但是我們可以從外部獲知嗎?為了嘗試這個,我們需要查看什麼臨時埠被映射到了Mongo的埠上,我們運行如下命令:

docker ps

從下面我們可以看到(為了可讀性省略了一些列):

CONTAINER ID IMAGE PORTS NAMES87192b65de95 mongo 0.0.0.0:32777->27017/tcp mongodb

我們可以看到宿主機埠32777映射到了容器埠27017上,然而,記住我們的容器是運行在虛擬機上的,所以我們必須回到我們的環境變量:

$ echo $DOCKER_HOSTtcp://192.168.99.100:2376

我們應該可以通過如下的地址訪問我們的Mongo容器的27017埠:
192.168.99.100:32777,啟動Mongo然後點擊那個位置顯示資料庫可以外部訪問:


本文為翻譯文章,閱讀原文,請點擊左下角閱讀原文連結。


相關焦點

  • 使用Spring Cloud和Docker構建微服務
    提供可以直接在生產環境中使用的功能,如性能指標、應用信息和應用健康檢查。沒有代碼生成,也沒有XML配置文件。每一個服務都含有一個特定意義的微服務架構。當你在Spring Cloud上構建微服務架構時,這裡有幾個基本概念需要首先澄清下。首先,你需要要先創建Configuration Service和Discovery Service兩個基礎服務。
  • 使用 Docker 部署 Spring Boot 項目
    Docker 技術發展為微服務落地提供了更加便利的環境,使用 Docker 部署 Spring Boot 其實非常簡單,這篇文章我們就來簡單學習下。首先構建一個簡單的 Spring Boot 項目,然後給項目添加 Docker 支持,最後對項目進行部署。
  • Spring Boot 2.0(四):使用 Docker 部署 Spring Boot
    Docker 技術發展為微服務落地提供了更加便利的環境,使用 Docker 部署 Spring Boot 其實非常簡單,這篇文章我們就來簡單學習下。
  • Netflix之後,如何用Spring Cloud 新組件構建微服務架構?
    不久前,Spring 正式發布新版本,該版本最大的變化是移除了多個之前處於維護模式的 Netflix 組件,如 Ribbon、Hystrix 和 Zuul。本文基於 Spring Cloud 新組件,闡述了如何構建微服務架構。
  • Spring Boot 分層構建 Docker 鏡像實戰
    直至 4⁄6 這部中,由於新的 Dockerfile 與原來 Dockerfile 發生變動,所以這部中間層鏡像直接是新創建的,並沒有使用緩存中間層鏡像。然後往下觀察,發現之後的全部構建都是新創建的中間層鏡像,即是腳本最後的一行和原來相同,也沒有使用緩存中間層鏡像。
  • Spring Cloud構建微服務架構:消息驅動的微服務(入門)【Dalston版】
    在該文中,我們通過簡單的配置和註解就能實現向RabbitMQ中生產和消費消息。實際上我們使用的對RabbitMQ的starter就是通過Spring Cloud Stream中對RabbitMQ的支持來實現的。下面我們就通過本文來了解一下Spring Cloud Stream。Spring Cloud Stream是一個用來為微服務應用構建消息驅動能力的框架。
  • Spring Boot 應用容器化之 Docker、Gradle
    ── docker_spring_boot創建 Gradle 構建文件build.gradlebuildscript {    repositories {        mavenCentral()    }    dependencies {        classpath('org.springframework.boot
  • 十年架構師用一文帶你搞懂微服務的協調者SpringCloud
    在1.2節中,我們討論了眾多的分布式系統的架構,可以說每種架構都有其優勢及局限,採用何種架構風格要看應用程式當前的使用場景。就微服務架構的風格而言,一套完整的微服務架構系統往往需要考慮以下挑戰。配置管理。服務註冊與發現。斷路器。智能路由。服務間調用。負載均衡。微代理。控制總線。
  • Docker+Jenkins+Seneca構建去集中化微服務架構
    而且服務發現式的架構處理不好,容易產生集中化。同時,微服務的提供,不可避免的需要一些負載均衡方案,實現服務的高可用和可擴展,這無疑增加了很多複雜度。筆者認為,使用異步、基於消息的方式,可能更適合微服務架構。基於消息的微服務架構,對於所有微服務的部署條件非常簡單,只需要能訪問到消息服務即可。同時微服務節點的移除和增加不會影響到服務的提供。相比服務發現的架構,簡單太多了,簡單即是美。
  • 使用Spring Boot Operator將Spring Boot部署到Kubernetes
    Java項目打包鏡像用Maven/Gradle插件比較多,我的另一篇文章構建Spring Boot的Docker鏡像[2]有介紹,這裡再介紹一個新的Google開源的插件Jib,該插件使用起來比較方便。
  • 如何使用 Node.js 和 Docker 構建高質量的微服務
    微服務的服務範圍越來越廣泛,尤其是在構建複雜應用中,下面我主要從以下幾點分享如何使用 Node.js 和 Docker 構建高質量的微服務
  • 使用Gradle構建SpringBoot項目Docker鏡像
    使用Gradle快速構建SpringBoot項目Docker鏡像,將鏡像推送到阿里雲遠程鏡像倉庫。
  • Spring Boot 使用 Graylog 收集日誌
    1.Graylog介紹Graylog是一個生產級別的日誌收集系統,集成Mongo和Elasticsearch進行日誌收集。其中Mongo用於存儲Graylog的元數據信息和配置信息,ElasticSearch用於存儲數據。
  • 雲原生時代的微服務架構實踐
    本書的創作重點,則是在於講述在巨多語言的情況下,該如何設計微服務架構,以及雲原生時代的微服務的高可用、自動化等等。目錄第一部分 基礎知識第一章 什麼是微服務架構1.1 微服務到底是啥1.2 微服務的發展史1.3 微服務的革命性與重要性第二章 微服務的拆分2.1 微服務的設計原則2.2 微服務劃分的粒度2.3 不同場景的微服務第三章 容器化技術3.1 什麼是容器3.2 容器的發展進程3.3 Docker 與 Kubernetes3.4 K8s 容器化應用第四章 為何藉助容器助力微服務4.1 微服務的多語言性
  • Spring Boot 2.x基礎教程:使用Swagger2構建強大的API文檔
    又碰到了運營號直接拿了文章就推,導致沒法標原創。隨著前後端分離架構和微服務架構的流行,我們使用Spring Boot來構建RESTful API項目的場景越來越多。通常我們的一個RESTful API就有可能要服務於多個不同的開發人員或開發團隊:IOS開發、Android開發、Web開發甚至其他的後端服務等。
  • 螞蟻開源的 SOFABoot,和 Spring Boot 有啥關係?
    「SOFAStack™(Scalable Open Financial Architecture Stack)是一套用於快速構建金融級雲原生架構的中間件,也是在金融場景裡錘鍊出來的最佳實踐。二、功能描述Spring Boot 雖然是一個非常優秀的主流開源框架,但在螞蟻內部會遇到很多問題,比如說 Spring Boot 在實施大規模微服務架構時候的就會遇到很多問題,所以 SOFABoot 應運而生。
  • 使用windows版Docker並在IntelliJ IDEA使用Docker運行Spring Cloud項目
    好的,在運行了第一個鏡像之後,我們要開始在IntelliJ IDEA中使用docker並構建我們的第一個spring boot程序放到docker中去運行IDEA的準備工作創建spring cloud項目 1.首先在Idea中創建一個spring boot項目,怎麼創建在此就不再贅述了創建完成之後,我們在pom.xml中添加依賴項
  • 優化Spring Boot應用Docker鏡像,提高CI/CD效率
    起,Spring Boot提供了spring-boot-jarmode-layertools的jar包,該jar將作為依賴項添加到應用的jar中。❝這個功能依賴spring-boot-maven-plugin插件。
  • Spring Boot (十九):使用 Spring Boot Actuator 監控應用
    微服務的特點決定了功能模塊的部署是分布式的,大部分功能模塊都是運行在不同的機器上,彼此通過服務調用進行交互,前後臺的業務流會經過很多個微服務的處理和傳遞,出現了異常如何快速定位是哪個環節出現了問題?在這種框架下,微服務的監控顯得尤為重要。
  • 微服務架構體系
    其實這時已經能算一種「微服務」了,一般會使用SpringBoot微服務和SOA微服務相比於SOA更加精細,微服務更多的以獨立的進程的方式存在,互相之間並無影響,不再需要協調其它服務部署對本服務的影響;微服務提供的接口方式更加通用化,如HTTP RESTful,各種終端都可以調用,無關語言、平臺,所以技術可以更隨意,只需要提供API微服務更傾向於分布式去中心化的部署方式