.Net Core微服務系列--配置中心
什麽是配置中心
簡單來說配置中心就是對配置進行管理的一個中心。對於配置這個司空見慣的東西,我們想想為什麽對於應用程序需要各種各樣的配置來支撐?
我們人類沒有辦法掌控和預知一切,所以映射到軟件系統這個領域,我們需要人為的預留一些線頭,以便在未來呢,撥弄這些線頭調整系統的飛行狀態。
而這些線頭就是我們程序中的配置,所以說配置是用來在程序運行狀態時動態調整行為的,比如說日誌級別,數據庫連接字符串等等。當然這只是一方面,另一方面我們需要方便的統一的對可變的參數進行管理也是配置的用武之地。
為什麽要有配置中心
以往可能用的最多的是配置文件,當需要修改配置內容時,我們會連接到服務器修改配置文件,重啟或者程序支持reload來達到修改配置的目的。但是微服務時代來臨了,我們面臨了哪些挑戰?
- 微服務是分布式的,如果我們有成千上萬的機器,是不是需要連接到這些機器上修改。
- 微服務每個服務是自治的,可以進行自由的技術選型,那麽配置文件的名字,配置文件存放的地方對於運維來說就是一個大難題。
- 成千上萬的機器修改了配置是不是同時生效?哪些配置生效了?哪些配置失敗了?
- 配置文件的回滾和容災機制
這些挑戰的提出,就催生了配置中心的出現。
配置中心需要支持的特性
當我們決定在項目中采用配置中心了,那麽我們可能需要確定下配置中心哪些特性才是我們迫切需要的。
高可用性
這個毋庸置疑了,整個系統的配置都是由配置中心來支撐,我們就必須保證配置中心的高可用性。
更新配置的及時性和成功率
當配置中心有配置更改了,能夠及時的通知客戶端並能保證一定的成功率
弱依賴和客戶端容災
雖然上面剛說了高可用性,但是我們設計程序應該想到的第一點就是他一定會掛,所以我們需要實現配置中心掛了的情況下應用程序依然能運行,這就需要我們在應用程序能有本地的配置存儲和一定的容災機制。
對應用程序的侵入小
應用程序需要獲取到配置中心的配置有三種實現方式
- Api 配置中心提供Api,應用程序通過Api進行配置的拉取,但是需要應用程序自己實現配置的緩存,容災和實時性的保證。
- SDK 配置中心給應用程序提供SDK,由SDK來控制配置的拉取,緩存,容災和保證實時性。
- Agent 由代理來進行配置的相關操作,應用程序通過進程或者Api跟agent進行通信。
當然最好的模式應該是Agent模式,所有行為都由代理來管理,對於應用程序基本沒有侵入。
配置的版本管理
這個可以實現配置的回滾
配置的灰度發布
可以跟灰度發布進行結合。
客戶端的配置監控
能實時監控到客戶端生效的配置
Apollo配置中心介紹
這篇文章我們選用Apollo來跟.Net Core集成實現配置中心
直接貼出官網對於Apollo的介紹
Apollo(阿波羅)是攜程框架部門研發的開源配置管理中心,能夠集中化管理應用不同環境、不同集群的配置,配置修改後能夠實時推送到應用端,並且具備規範的權限、流程治理等特性。
Apollo支持4個維度管理Key-Value格式的配置:
- application (應用)
- environment (環境)
- cluster (集群)
- namespace (命名空間)
同時,Apollo基於開源模式開發,開源地址:https://github.com/ctripcorp/apollo
Apollo從設計之初就立誌於成為一個有治理能力的配置發布平臺,目前提供了以下的特性:
- 統一管理不同環境、不同集群的配置
- Apollo提供了一個統一界面集中式管理不同環境(environment)、不同集群(cluster)、不同命名空間(namespace)的配置。
- 同一份代碼部署在不同的集群,可以有不同的配置,比如zookeeper的地址等
- 通過命名空間(namespace)可以很方便地支持多個不同應用共享同一份配置,同時還允許應用對共享的配置進行覆蓋
- 配置修改實時生效(熱發布)
- 用戶在Apollo修改完配置並發布後,客戶端能實時(1秒)接收到最新的配置,並通知到應用程序
- 版本發布管理
- 所有的配置發布都有版本概念,從而可以方便地支持配置的回滾
- 灰度發布
- 支持配置的灰度發布,比如點了發布後,只對部分應用實例生效,等觀察一段時間沒問題後再推給所有應用實例
- 權限管理、發布審核、操作審計
- 應用和配置的管理都有完善的權限管理機制,對配置的管理還分為了編輯和發布兩個環節,從而減少人為的錯誤。
- 所有的操作都有審計日誌,可以方便地追蹤問題
- 客戶端配置信息監控
- 可以在界面上方便地看到配置在被哪些實例使用
- 提供Java和.Net原生客戶端
- 提供了Java和.Net的原生客戶端,方便應用集成
- 支持Spring Placeholder, Annotation和Spring Boot的ConfigurationProperties,方便應用使用(需要Spring 3.1.1+)
- 同時提供了Http接口,非Java和.Net應用也可以方便地使用
- 提供開放平臺API
- Apollo自身提供了比較完善的統一配置管理界面,支持多環境、多數據中心配置管理、權限、流程治理等特性。不過Apollo出於通用性考慮,不會對配置的修改做過多限制,只要符合基本的格式就能保存,不會針對不同的配置值進行針對性的校驗,如數據庫用戶名、密碼,Redis服務地址等
- 對於這類應用配置,Apollo支持應用方通過開放平臺API在Apollo進行配置的修改和發布,並且具備完善的授權和權限控制
- 部署簡單
- 配置中心作為基礎服務,可用性要求非常高,這就要求Apollo對外部依賴盡可能地少
- 目前唯一的外部依賴是MySQL,所以部署非常簡單,只要安裝好Java和MySQL就可以讓Apollo跑起來
- Apollo還提供了打包腳本,一鍵就可以生成所有需要的安裝包,並且支持自定義運行時參數
從特性上來看,滿足了我們前面所需要的特性,所以直接開始進入正題。
使用Docker部署Apollo
本次使用的是單機部署,如果正式環境請參考分布式部署指南
首先到git上clone代碼,可能有點慢,耐心等待下或者直接去docker-quick-start中把sql文件夾和compose文件下載下來
git clone [email protected]:ctripcorp/apollo.git
修改Compose文件,把被占用的端口修改一下,我這裏把8080改為18080,8070改為18070,接著直接運行命令
docker-compose up
第一次運行需要下載鏡像可能會慢一點,運行成功的截圖如下:
在瀏覽器中訪問IP:18080 賬號:apollo 密碼:admin 登陸apollo
連接Mysql
可以看到Apollo默認有兩個數據庫
貼一下官方對幾個核心概念的解釋
- application (應用)
- 這個很好理解,就是實際使用配置的應用,Apollo客戶端在運行時需要知道當前應用是誰,從而可以去獲取對應的配置
- 每個應用都需要有唯一的身份標識 -- appId,我們認為應用身份是跟著代碼走的,所以需要在代碼中配置,具體信息請參見Java客戶端使用指南。
- environment (環境)
- 配置對應的環境,Apollo客戶端在運行時需要知道當前應用處於哪個環境,從而可以去獲取應用的配置
- 我們認為環境和代碼無關,同一份代碼部署在不同的環境就應該能夠獲取到不同環境的配置
- 所以環境默認是通過讀取機器上的配置(server.properties中的env屬性)指定的,不過為了開發方便,我們也支持運行時通過System Property等指定,具體信息請參見Java客戶端使用指南。
- cluster (集群)
- 一個應用下不同實例的分組,比如典型的可以按照數據中心分,把上海機房的應用實例分為一個集群,把北京機房的應用實例分為另一個集群。
- 對不同的cluster,同一個配置可以有不一樣的值,如zookeeper地址。
- 集群默認是通過讀取機器上的配置(server.properties中的idc屬性)指定的,不過也支持運行時通過System Property指定,具體信息請參見Java客戶端使用指南。
- namespace (命名空間)
- 一個應用下不同配置的分組,可以簡單地把namespace類比為文件,不同類型的配置存放在不同的文件中,如數據庫配置文件,RPC配置文件,應用自身的配置文件等
- 應用可以直接讀取到公共組件的配置namespace,如DAL,RPC等
- 應用也可以通過繼承公共組件的配置namespace來對公共組件的配置做調整,如DAL的初始數據庫連接數
創建一個測試項目 填寫應用ID(應用的唯一標識,客戶端需要使用)
可以看到有一個application的默認namespace,直接添加幾個測試配置,並發布
OK,Apollo的簡單的配置到此結束
創建.Net Core 項目並集成Apollo
創建一個webapi
dotnet new webapi
使用Nuget安裝package
Install-Package Com.Ctrip.Framework.Apollo.Configuration
appsettings.json添加節點,MetaServer是Apollo的地址
"apollo": {
"AppId": "testservice",
"MetaServer": "http://{IP}:18080",
"Env": "DEV"
}
修改Program.cs
WebHost.CreateDefaultBuilder(args)
+ .ConfigureAppConfiguration(builder => builder
+ .AddApollo(builder.Build().GetSection("apollo"))
+ .AddDefault() //添加默認application Namespace
)
.UseStartup<Startup>();
修改ValuesController
private readonly IConfiguration _configuration;
public ValuesController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
var appName = _configuration["AppName"];
return new string[] { "value1", "value2", appName };
}
OK, 集成很簡單的,直接運行查看結果
果然沒那麽順利。。獲取不到配置的值,修改一下代碼,在CreateWebHostBuilder中加入logProvider
Com.Ctrip.Framework.Apollo.Logging.LogManager.Provider = new ConsoleLoggerProvider(Com.Ctrip.Framework.Apollo.Logging.LogLevel.Trace);
可以看到,客戶端是從Docker的內部地址去獲取數據,這個IP 應該是從Apollo的服務發現工具Eureka獲取的IP。
查看官方文檔分布式部署中有解決方案
分布式部署的時候,
apollo-configservice
和apollo-adminservice
需要把自己的IP和端口註冊到Meta Server(apollo-configservice本身)。Apollo客戶端和Portal會從Meta Server獲取服務的地址(IP+端口),然後通過服務地址直接訪問。
所以如果實際部署的機器有多塊網卡(如docker),或者存在某些網卡的IP是Apollo客戶端和Portal無法訪問的(如網絡安全限制),那麽我們就需要在
apollo-configservice
和apollo-adminservice
中做相關限制以避免Eureka將這些網卡的IP註冊到Meta Server。具體文檔可以參考Ignore Network Interfaces章節。具體而言,就是分別編輯apollo-configservice/src/main/resources/application.yml和apollo-adminservice/src/main/resources/application.yml,然後把需要忽略的網卡加進去。
如下面這個例子就是對於
apollo-configservice
,把docker0和veth.*的網卡在註冊到Eureka時忽略掉。spring: application: name: apollo-configservice profiles: active: ${apollo_profile} cloud: inetutils: ignoredInterfaces: - docker0 - veth.*
註意,對於application.yml修改時要小心,千萬不要把其它信息改錯了,如spring.application.name等。
另外一種方式是直接指定要註冊的IP,可以修改startup.sh,通過JVM System Property在運行時傳入,如
-Deureka.instance.ip-address=${指定的IP}
,或者也可以修改apollo-adminservice或apollo-configservice 的bootstrap.yml文件,加入以下配置eureka: instance: ip-address: ${指定的IP}
最後一種方式是直接指定要註冊的IP+PORT,可以修改startup.sh,通過JVM System Property在運行時傳入,如
-Deureka.instance.homePageUrl=http://${指定的IP}:${指定的Port}
,或者也可以修改apollo-adminservice或apollo-configservice 的bootstrap.yml文件,加入以下配置eureka: instance: homePageUrl: http://${指定的IP}:${指定的Port} preferIpAddress: false
做完上述修改並重啟後,可以查看Eureka頁面(http://${config-service-url:port})檢查註冊上來的IP信息是否正確。
如果Apollo部署在公有雲上,本地開發環境無法連接,但又需要做開發測試的話,客戶端可以升級到0.11.0版本及以上,然後通過
-Dapollo.configService=http://config-service的公網IP:端口
來跳過meta service的服務發現
我這裏直接選用的第二種方式也就是修改Deureka.instance.ip-address為指定的IP
首先修改Compose文件,將8090端口暴露出來,然後重新運行compose文件
進入容器
docker exec it ${容器Id} /bin/bash
進入到執行路徑,並修改demo.sh,在106行 JAVA_OPTS 後面字符串中加入-Deureka.instance.ip-address=${指定的IP}
cd apollo-quick-start
vi demo.sh
退出容器並重啟容器。
OK,重新運行程序,並在瀏覽器中訪問 https://localhost:5001/api/Values,可以看到已經獲取到配置值
接下來 修改配置AppName 為 DemoService並發布,刷新瀏覽器可以看到值已經修改成功了。
小結
今天試用了下Apollo的簡單集成,沒有用到很深入的東西,只是一個嘗試,後續會結合其他工具一起進行深入的了解。
.Net Core微服務系列--配置中心