天天看點

實戰中的asp.net core結合Consul叢集&Docker實作服務治理二、部署consul叢集 三、asp.net core微服務進行服務注冊

<h1><span style="color: #3194d0;">0、目錄</span></h1>           

整體架構目錄:

ASP.NET Core分布式項目實戰-目錄

一、前言

在寫這篇文章之前,我看了很多關于consul的服務治理,但發現基本上都是直接在powershell或者以指令工具的方式在伺服器上面直接輸入consul agent .... 來搭建啟動consul叢集,一旦把指令工具關掉,則consul無法再背景啟動,尤其是在linux系統中。

如果在window系統中,采用bat檔案到時可以做成開機自啟,或者在linux中把指令做成一個service 服務檔案來啟動就可以實作背景運作。

是以為了社群能更好的發展技術,且能把consul應用在生産環境中,我認真回顧了在公司項目中的應用,特此把我開發及總結的分享給大家,此篇文章采用docker引擎,并且隻涉及到consul叢集部署以及結合asp.net core微服務注冊。

當然如果内容有涉及安全隐患的,大家可以積極留言,共同進步。

 注:以下所涉及到部署過程全部經過樓主我反複的驗證,總結得來。大家在應用的過程中如果出現問題,可以留言咨詢。

二、部署consul叢集

1、介紹一下Consul

consul 關鍵特性

  服務發現:支援服務發現。你可以通過 DNS 或 HTTP 的方式擷取服務資訊。

  健康檢查:支援健康檢查。可以提供與給定服務相關聯的任何數量的健康檢查(如 web 狀态碼或 cpu 使用率)。

  K/V 存儲:鍵/值對存儲。你可用通過 consul 存儲如動态配置之類的相關資訊。

  多資料中心:支援多資料中心,開箱即用。

  WEB UI:支援 WEB UI。點點點,你就能夠了解你的服務現在的運作情況,一目了然,對開發運維是非常友好的。

說明:

在Consul方案中,每個提供服務的節點上都要部署和運作Consul的Client Agent,所有運作Consul Agent節點的集合構成Consul Cluster。

Consul Agent有兩種運作模式:Server和Client。這裡的Server和Client隻是Consul叢集層面的區分,與搭建在Cluster之上的應用服務無關。以Server模式運作的Consul Agent節點用于維護Consul叢集的狀态,官方建議每個Consul Cluster至少有3個或以上的運作在Server Mode的Agent,Client節點不限。

Consul支援多資料中心,每個資料中心的Consul Cluster都會在運作于Server模式下的Agent節點中選出一個Leader節點,這個選舉過程通過Consul實作的raft協定保證,多個 Server節點上的Consul資料資訊是強一緻的。處于Client Mode的Consul Agent節點比較簡單,無狀态,僅僅負責将請求轉發給Server Agent節點。

Consul與其他工具的比較

實戰中的asp.net core結合Consul叢集&amp;Docker實作服務治理二、部署consul叢集 三、asp.net core微服務進行服務注冊

consul的端口解釋

實戰中的asp.net core結合Consul叢集&amp;Docker實作服務治理二、部署consul叢集 三、asp.net core微服務進行服務注冊

好了,該介紹的已經介紹了,接下來就是正式部署吧

2、Docker上運作Consul

第一步:安全配置,适用授權和資料加密傳輸(生産環境是必須的)

為了consul的安全,需要進行gossip加密以及RPC加密結合TLS。

Gossip加密:主要用于節點間的接收發送叢集資訊安全。

RPC加密:主要用于Agent之間調用RPC授權的安全性。

此處大家可以參考官網或者一下資料:

https://www.jianshu.com/p/3d074ed76a68

https://www.consul.io/docs/agent/encryption.html

第二步:consul叢集部署

準備伺服器(可以采用虛拟機部署)

實戰中的asp.net core結合Consul叢集&amp;Docker實作服務治理二、部署consul叢集 三、asp.net core微服務進行服務注冊

部署server1

docker run -d --net=host -v $PWD/data:/consul/data -v $PWD/config:/consul/config --restart always --name consulServer1 consul agent -server -bind=192.168.216.160 -node=server1 -bootstrap-expect 3 -data-dir=/consul/data -config-file=/consul/config -client 0.0.0.0 -ui

部署server2

docker run -d --net=host -v $PWD/data:/consul/data -v $PWD/config:/consul/config --restart always --name cs2 consul agent -server -bind=192.168.216.161 -node=server2 -bootstrap-expect 3 -data-dir=/consul/data -config-file=/consul/config -client 0.0.0.0 -ui -retry-join=192.168.216.160

部署server3

docker run -d --net=host -v $PWD/data:/consul/data -v $PWD/config:/consul/config --restart always --name cs3 consul agent -server -bind=192.168.216.162 -node=server3 -bootstrap-expect 3 -data-dir=/consul/data -config-file=/consul/config -client 0.0.0.0 -ui -retry-join=192.168.216.160

部署client-連接配接server

docker run -d --net=host -v $PWD/data:/consul/data -v $PWD/config:/consul/config --restart always --name cc1 consul agent -bind=192.168.216.163 -node=client1 -data-dir=/consul/data -config-file=/consul/config -client 0.0.0.0 -retry-join=192.168.216.160

注:(針對以上的參數以及配置解釋)

1、此處的資料挂載就是把容器内的data以及配置挂載到外部位址,$PWD代表目前目錄即你運作docker指令時的目錄,當然$PWD可以替換為具體的路徑。

2、-data-dir=/consul/consul -config-file=/consul/config :這兩個地方代表在consul啟動後會把資料以及配置檔案放到指定的目錄下,這個對于在伺服器模式下運作的代理尤其重要,因為它們必須能夠保持群集狀态。

3、總結,以上兩點結合就是說明 consul産生的資料然後挂載在外部位址進行持久化存儲。

4、server端 的-client 參數可以不用,官方推薦是 服務注冊到client端,由client端再把資料統一送出到server端。

參數說明

-client : 0.0.0.0 代表綁定到所有接口的選項,如果沒有此選項,則asp.net core無法進行服務注冊使用

-bind :該位址用來在叢集内部的通訊,叢集内的所有節點到位址都必須是可達的

--net=host 使得docker容器越過了net namespace的隔離,免去手動指定端口映射的步驟

-retry-join 允許你在第一次失敗後進行嘗試,加入一個已經啟動的agent的ip位址

-bootstrap-expect 提供的server節點數目

-ui 啟動自有主機的界面

-data-dir :提供一個目錄用來存放agent的狀态,所有的agent允許都需要該目錄,該目錄必須是穩定的,系統重新開機後都繼續存在

CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}:在0.7版本之後預設是true,是以不是必須的。

CONSUL_LOCAL_CONFIG={"leave_on_terminate": true}:在0.7版本之後預設是true,是以不是必須的。如果啟用,當代理收到TERM信号時,它将向Leave群集的其餘部分發送消息并正常離開

打開浏覽器器檢視 consul :

實戰中的asp.net core結合Consul叢集&amp;Docker實作服務治理二、部署consul叢集 三、asp.net core微服務進行服務注冊

看所有consul節點情況

docker exec -t cs1 consul members

實戰中的asp.net core結合Consul叢集&amp;Docker實作服務治理二、部署consul叢集 三、asp.net core微服務進行服務注冊

檢視server的狀态,以及哪一個節點是leader

docker exec -t cs1 consul operator raft list-peers

實戰中的asp.net core結合Consul叢集&amp;Docker實作服務治理二、部署consul叢集 三、asp.net core微服務進行服務注冊

注:

如果其中一台或者多台server端挂掉,則Consul叢集就會重新選舉新的Leader,

但是一旦挂掉阿的節點數量超過一半,則Consul叢集就無法工作了。

三、asp.net core微服務進行服務注冊

1、建立一個項目asp.net core webapi項目

引入 consul ,nuget包

2、在控制器中建立一個控制器Health,代表健康檢查

[Produces("application/json")]      
[Route(</span><span style="color: #800000;">"</span><span style="color: #800000;">api/Health</span><span style="color: #800000;">"</span><span style="color: #000000;">)]
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> HealthController : Controller
{
    [HttpGet]
    </span><span style="color: #0000ff;">public</span> IActionResult Get() =&gt; Ok(<span style="color: #800000;">"</span><span style="color: #800000;">ok</span><span style="color: #800000;">"</span><span style="color: #000000;">);
}</span></pre>           

3、然後在appsetting.json中添加配置檔案

需要手動配置一下位址和端口号。

{
"Logging": {      
</span><span style="color: #800000;">"</span><span style="color: #800000;">IncludeScopes</span><span style="color: #800000;">"</span>: <span style="color: #0000ff;">false</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">Debug</span><span style="color: #800000;">"</span><span style="color: #000000;">: {
  </span><span style="color: #800000;">"</span><span style="color: #800000;">LogLevel</span><span style="color: #800000;">"</span><span style="color: #000000;">: {
    </span><span style="color: #800000;">"</span><span style="color: #800000;">Default</span><span style="color: #800000;">"</span>: <span style="color: #800000;">"</span><span style="color: #800000;">Warning</span><span style="color: #800000;">"</span><span style="color: #000000;">
  }
},
</span><span style="color: #800000;">"</span><span style="color: #800000;">Console</span><span style="color: #800000;">"</span><span style="color: #000000;">: {
  </span><span style="color: #800000;">"</span><span style="color: #800000;">LogLevel</span><span style="color: #800000;">"</span><span style="color: #000000;">: {
    </span><span style="color: #800000;">"</span><span style="color: #800000;">Default</span><span style="color: #800000;">"</span>: <span style="color: #800000;">"</span><span style="color: #800000;">Warning</span><span style="color: #800000;">"</span><span style="color: #000000;">
  }
}           

},

"ServiceRegister": { //服務注冊

<span style="color: #800000;">"</span><span style="color: #800000;">IsActive</span><span style="color: #800000;">"</span>: <span style="color: #0000ff;">true</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">ServiceName</span><span style="color: #800000;">"</span>: <span style="color: #800000;">"</span><span style="color: #800000;">testconsul6</span><span style="color: #800000;">"</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">ServiceHost</span><span style="color: #800000;">"</span>: <span style="color: #800000;">"</span><span style="color: #800000;">192.168.216.163</span><span style="color: #800000;">"</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">ServicePort</span><span style="color: #800000;">"</span>: <span style="color: #800080;">5006</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">Register</span><span style="color: #800000;">"</span><span style="color: #000000;">: {
  </span><span style="color: #800000;">"</span><span style="color: #800000;">HttpEndpoint</span><span style="color: #800000;">"</span>: <span style="color: #800000;">"</span><span style="color: #800000;">http://192.168.216.163:8500</span><span style="color: #800000;">"</span><span style="color: #000000;">
}           

}

4、這一步是服務注冊的類

ServiceRegisterOptions.cs

public class ServiceRegisterOptions      
{
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;summary&gt;</span>
    <span style="color: #808080;">///</span><span style="color: #008000;"> 是否啟用
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;/summary&gt;</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">bool</span> IsActive { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; }
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;summary&gt;</span>
    <span style="color: #808080;">///</span><span style="color: #008000;"> 服務名稱
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;/summary&gt;</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">string</span> ServiceName { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; }
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;summary&gt;</span>
    <span style="color: #808080;">///</span><span style="color: #008000;"> 服務IP或者域名
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;/summary&gt;</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">string</span> ServiceHost { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; }
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;summary&gt;</span>
    <span style="color: #808080;">///</span><span style="color: #008000;"> 服務端口号
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;/summary&gt;</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span> ServicePort { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; }
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;summary&gt;</span>
    <span style="color: #808080;">///</span><span style="color: #008000;"> consul注冊位址
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;/summary&gt;</span>
    <span style="color: #0000ff;">public</span> RegisterOptions Register { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; }
}</span></pre>           

 RegisterOptions.cs

public class RegisterOptions      
{
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">string</span> HttpEndpoint { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; }
}</span></pre>           

 5、在startup.cs 中的服務注冊

public void ConfigureServices(IServiceCollection services)      
{
        </span><span style="color: #0000ff;">#region</span> 服務注冊基礎資訊配置<span style="color: #000000;">
        services.Configure</span>&lt;ServiceRegisterOptions&gt;(Configuration.GetSection(<span style="color: #800000;">"</span><span style="color: #800000;">ServiceRegister</span><span style="color: #800000;">"</span><span style="color: #000000;">));
        services.AddSingleton</span>&lt;IConsulClient&gt;(p =&gt; <span style="color: #0000ff;">new</span> ConsulClient(cfg =&gt;<span style="color: #000000;">
        {
            </span><span style="color: #0000ff;">var</span> serviceConfiguration = p.GetRequiredService&lt;IOptions&lt;ServiceRegisterOptions&gt;&gt;<span style="color: #000000;">().Value;
            </span><span style="color: #0000ff;">if</span> (!<span style="color: #0000ff;">string</span><span style="color: #000000;">.IsNullOrEmpty(serviceConfiguration.Register.HttpEndpoint))
            {
                cfg.Address </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Uri(serviceConfiguration.Register.HttpEndpoint);
            }
        }));
        </span><span style="color: #0000ff;">#endregion</span><span style="color: #000000;">

        services.AddMvc();
    }</span></pre>           

在Configure 方法中配置代碼

private static void RegisterService(IApplicationBuilder app,      
IOptions</span>&lt;ServiceRegisterOptions&gt;<span style="color: #000000;"> serviceRegisterOptions,
        IConsulClient consul,
        IApplicationLifetime appLife)
    {
        </span><span style="color: #0000ff;">var</span> serviceId = $<span style="color: #800000;">"</span><span style="color: #800000;">{serviceRegisterOptions.Value.ServiceName}_{serviceRegisterOptions.Value.ServiceHost}:{serviceRegisterOptions.Value.ServicePort}</span><span style="color: #800000;">"</span><span style="color: #000000;">;

        </span><span style="color: #0000ff;">var</span> httpCheck = <span style="color: #0000ff;">new</span><span style="color: #000000;"> AgentServiceCheck()
        {
            DeregisterCriticalServiceAfter </span>= TimeSpan.FromSeconds(<span style="color: #800080;">5</span>),<span style="color: #008000;">//</span><span style="color: #008000;">服務啟動多久後注冊</span>
            Interval = TimeSpan.FromSeconds(<span style="color: #800080;">30</span>),<span style="color: #008000;">//</span><span style="color: #008000;">健康檢查時間間隔,或者稱為心跳間隔</span>
            HTTP = $<span style="color: #800000;">"</span><span style="color: #800000;">http://{serviceRegisterOptions.Value.ServiceHost}:{serviceRegisterOptions.Value.ServicePort}/api/health</span><span style="color: #800000;">"</span>,<span style="color: #008000;">//</span><span style="color: #008000;">健康檢查位址</span>           

};

</span><span style="color: #0000ff;">var</span> registration = <span style="color: #0000ff;">new</span><span style="color: #000000;"> AgentServiceRegistration()
        {
            Checks </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;">[] { httpCheck },
            Address </span>=<span style="color: #000000;"> serviceRegisterOptions.Value.ServiceHost,
            ID </span>=<span style="color: #000000;"> serviceId,
            Name </span>=<span style="color: #000000;"> serviceRegisterOptions.Value.ServiceName,
            Port </span>=<span style="color: #000000;"> serviceRegisterOptions.Value.ServicePort
            </span><span style="color: #008000;">//</span><span style="color: #008000;">Tags = new[] { $"urlprefix-/{serviceRegisterOptions.Value.ServiceName}" }</span><span style="color: #008000;">//</span><span style="color: #008000;">添加 urlprefix-/servicename 格式的 tag 标簽,以便 Fabio 識别</span>           
</span><span style="color: #008000;">//</span><span style="color: #008000;">.GetAwaiter().GetResult()</span>           

consul.Agent.ServiceRegister(registration).GetAwaiter().GetResult();

appLife.ApplicationStopping.Register(() </span>=&gt;<span style="color: #000000;">
        {
            consul.Agent.ServiceDeregister(serviceId).GetAwaiter().GetResult();</span><span style="color: #008000;">//</span><span style="color: #008000;">服務停止時取消注冊</span>           

});

}
    </span><span style="color: #0000ff;">#endregion</span></pre>           

自此,可以把我們的項目部署到163伺服器上面的docker上啦。

我們看一下效果,成功注冊到了

實戰中的asp.net core結合Consul叢集&amp;Docker實作服務治理二、部署consul叢集 三、asp.net core微服務進行服務注冊
實戰中的asp.net core結合Consul叢集&amp;Docker實作服務治理二、部署consul叢集 三、asp.net core微服務進行服務注冊

參考資料:

Conusl TLS配置:

consul 參數配置:

https://blog.csdn.net/zl1zl2zl3/article/details/79622476

consul服務治理:

http://michaco.net/blog/ServiceDiscoveryAndHealthChecksInAspNetCoreWithConsul

https://www.cnblogs.com/myzony/p/9168851.html

https://www.cnblogs.com/edisonchou/p/9124985.html

文章轉載自阿裡雲 MVP郭聯钰,

檢視原文

繼續閱讀