基於Ocelot、Consul、Registrator搭建微服務架構,實現服務發現和自動註冊,後續逐步完善許可權驗證、負載均衡等功能。 ...
目錄
關於這個工具的介紹這裡就不多說了,網上、官網都很詳細,這裡直接記錄一下搭建過程。另外最後有幾個疑惑還未解決,希望各位前輩答疑解惑。
1. Consul集群搭建
我們基於Docker搭建三個Server和兩個Client的DC。
- server1
docker run -d --name server1 consul agent -server -node=server1 -bootstrap-expect=3
# 獲取server1IP地址,方便後續節點接入集群
JOIN_IP="$(sudo docker inspect -f '{{.NetworkSettings.IPAddress}}' server1)"
- server2
docker run -d --name server2 consul agent -server -node=server2 -join $JOIN_IP
- server3
docker run -d --name server3 consul agent -server -node=server3 -join $JOIN_IP
- client1
docker run -d --name client1 consul agent -node=client1 -join $JOIN_IP
- client2
docker run -d --name client2 -p 8400:8400 -p 8500:8500 -p 8600:53/udp consul agent -ui -node=client2 -client=0.0.0.0 -join $JOIN_IP
運行效果:
1.1 F&Q
Consul官方推薦的host網路模式運行
查看Consul官方文檔,官方推薦Consul以Docker的host網路模式運行。但是這樣的話,一個Docker寄宿主機只運行一個consul agent?生產中的Docker集群是如何搭建的?
2. Registrator服務註冊工具
啟動Registrator工具容器,Registrator使用主機網路模式,使用-internal
選項,註冊基於expose埠的服務。
docker run -d --name registrator --net host --volume=/var/run/docker.sock:/tmp/docker.sock gliderlabs/registrator -internal consul://172.17.0.6:8500
Consul管理UI服務截圖,Registrator會把Consul也作為服務註冊到Consul。這也是可以理解的,因為Registrator的觀點是任何監聽在埠的程式都是服務。
2.1 F&Q
Registrator懸掛服務
正常情況下,這裡應該是沒有clientservice的,但是由於我之前測試沒有正常按順序啟停容器,形成了懸掛服務,一直沒有清除。我看Registrator文檔中有
-cleanup
選項可以清理懸掛服務,後續嘗試一下。
Registrator的-internal
選項
不指定這個選項時,Registrator使用容器的發佈埠(映射到寄宿主機的埠)自動註冊服務,這時候一般要用
-ip
選項指定主機的IP地址,因為Registrator一般很難推斷出主機的IP地址。當使用-internal
選項時,Registrator使用服務所在容器分配的內部地址自動註冊服務。
3. clientservice服務Demo
clientservice是一個web api項目,由於是自動服務發現和註冊,因此clientservice跟無需做特別的配置。
我僅僅指定服務的埠為5000,修改了ValueController,返回服務調用情況。
3.1 Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseUrls("http://*:5000")
.UseStartup<Startup>();
}
3.2 ValuesController.cs
...
[HttpGet]
public ActionResult<RequestInfo> Get()
{
var clientHost = HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
var serverHost = HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString();
var path = HttpContext.Request.Path;
return new RequestInfo{
RemoteIp = clientHost,
RemotePort = HttpContext.Connection.RemotePort,
LocalIp = serverHost,
LocalPort = HttpContext.Connection.LocalPort,
Path = path,
Host = HttpContext.Request.Host.ToUriComponent()
};
}
...
3.3 Dockerfile
#基於microsoft/dotnet:latest構建Docker image
FROM microsoft/dotnet:latest
#進入docker中的/usr/local/src目錄,創建DockerWebAPI目錄
RUN cd /usr/local/src&&mkdir DockerWebAPI
#設置工作路徑
WORKDIR /usr/local/src/DockerWebAPI
#將當前文件夾下的所有文件全部複製到工作目錄
COPY ./ ./
#向外界暴露5000埠
EXPOSE 5000
#執行dotnet *.dll命令
CMD ["dotnet","clientservice.dll"]
3.4 製作鏡像並啟動容器
- 製作鏡像
docker build -t zhangdk/clientservice .
- 啟動容器
docker run -d zhangdk/clientservice
Consul管理UI截圖,如果你是第一次搭建集群並啟動Clientservice的話,後面應該有小標1。我這裡由於懸掛服務的問題,有啟動前的1現在變成了2。
clientservice註冊信息(多餘的是懸掛服務的註冊信息),要註意查看ServiceAddress和ServicePort是否正確:
4. Ocelot網關Demo
VS Code創建空web項目dotnet new web
,安裝ocelot庫dot add package ocelot
,主要修改三個文件:
4.1 Program.cs:
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseUrls("http://*:5010") //服務監聽在5010埠,後期可以調整為從外部讀取,方便配置
.ConfigureAppConfiguration((context,builder)=>{
builder.AddJsonFile("configuration.json",false,true); //讀取Ocelot配置文件
});
}
4.2 Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration){
Configuration = configuration;
}
public IConfiguration Configuration{get;set;}
public void ConfigureServices(IServiceCollection services) => services.AddOcelot(Configuration); //註冊Ocelot服務
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseOcelot().Wait(); //啟用Ocelot中間件
}
}
4.3 添加配置文件configuration.json:
{
"ReRoutes": [
{
"UseServiceDiscovery": true, //啟用服務發現
"DownstreamPathTemplate": "/api/{url}",
"DownstreamScheme": "http",
"ServiceName": "clientservice",
"LoadBalancerOptions": {
"Type": "RoundRobin"
},
"UpstreamPathTemplate": "/api/clientservice/{url}",
"UpstreamHttpMethod": [ "Get", "Post" ],
"ReRoutesCaseSensitive": false
}
],
"GlobalConfiguration": {
"ServiceDiscoveryProvider": {
"Host": "172.17.0.6", //Consul client IP
"Port": 8500 //Consul HTTP API埠
}
}
}
4.4 Dockerfile文件編寫
#基於microsoft/dotnet:latest構建Docker image
FROM microsoft/dotnet:latest
#進入docker中的/usr/local/src目錄,創建DockerWebAPI目錄
RUN cd /usr/local/src&&mkdir DockerWebAPI
#設置工作路徑
WORKDIR /usr/local/src/DockerWebAPI
#將當前文件夾下的所有文件全部複製到工作目錄
COPY ./ ./
#向外界暴露5010埠
EXPOSE 5010
#執行dotnet *.dll命令
CMD ["dotnet","oceletgateway.dll"]
4.5 發佈項目,製作鏡像,並啟動容器
- 發佈項目
dotnet publish
- 拷貝Dockfile到Demo的publish文件夾,打開終端
docker build -t zhangdk/ocelotgateway .
- 運行OcelotGateway服務容器
docker run -d --name=ocelotgateway zhangdk/ocelotgateway
通過Ocelot網關服務訪問clientservice
直接訪問clientservice
4.6 F&Q
Ocelot服務發現地址怎麼配置
剛開始的我以為應該配置Leader server的地址,但是由於leader server的埠並沒有暴露,因此容器間無法訪問。嘗試使用client的地址,發現也是可以成功的。
Ocelot的服務發現地址配置
Consul在集群模式運行時,單個server或者client agent節點掛掉後仍然可以通過投票等協議正常運行。但是,Ocelot網關服務中配置了唯一的一個agent地址,那豈不是意味著這個節點掛掉後,Ocelot網關服務就和Consul集群的服務發現就失去了聯繫,那這裡的Consul集群有什麼意義?
5 總結
初步按照自己的思路搭建起來的服務發現,自動註冊的架構,後續會繼續完善許可權驗證、負載均衡等網關的其他功能。搭建的過程中也有許多困惑還沒有想明白,後續邊想邊做。微服務這一塊也是剛剛接觸,有什麼問題或錯誤,還希望各位前輩指正。