基于Docker的Consul服务发现集群搭建
在去年的.NET Core微服务系列文章中,初步学习了一下Consul服务发现,总结了两篇文章。本次基于Docker部署的方式,以一个Demo示例来搭建一个Consul的示例集群,最后给出一个HA的架构示范,也会更加贴近于实际应用环境。
一、示例整体架构
此示例会由一个API Gateway, 一个Consul Client以及三个Consul Server组成,有关Consul的Client和Server这两种模式的Agent的背景知识,请移步我之前的文章加以了解:《.NET Core微服务之基于Consul实现服务治理》。其中,Consul的Client和Server节点共同构成一个Data Center,而API Gateway则从Consul中获取到服务的IP和端口号,并返回给服务消费者。这里的API Gateway是基于Ocelot来实现的,它不是这里的重点,也就不过多说明了,不了解的朋友请移步我的另一篇:《.NET Core微服务之基于Ocelot实现API网关服务》。
二、Consul集群搭建
2.1 Consul镜像拉取
docker pull consul:1.4.4
验证:docker images
2.2 Consul Server实例创建
以下我的实践是在一台机器上(CentOS 7)操作的,因此将三个实例分别使用了不同的端口号(区别于默认端口号8500)。实际环境中,建议多台机器部署。
(1)Consul实例1
docker run -d -p 8510:8500 --restart=always -v /XiLife/consul/data/server1:/consul/data -v /XiLife/consul/conf/server1:/consul/config -e CONSUL_BIND_INTERFACE='eth0' --privileged=true --name=consul_server_1 consul:1.4.4 agent -server -bootstrap-expect=3 -ui -node=consul_server_1 -client='0.0.0.0' -data-dir /consul/data -config-dir /consul/config -datacenter=xdp_dc;
(2)Consul实例2
为了让Consul实例2加入集群,首先获取一下Consul实例1的IP地址:
JOIN_IP="$(docker inspect -f '{{.NetworkSettings.IPAddress}}' consul_server_1)";
docker run -d -p 8520:8500 --restart=always -v /XiLife/consul/data/server2:/consul/data -v /XiLife/consul/conf/server2:/consul/config -e CONSUL_BIND_INTERFACE='eth0' --privileged=true --name=consul_server_2 consul:1.4.4 agent -server -ui -node=consul_server_2 -client='0.0.0.0' -datacenter=xdp_dc -data-dir /consul/data -config-dir /consul/config -join=$JOIN_IP;
(3)Consul实例3
docker run -d -p 8530:8500 --restart=always -v /XiLife/consul/data/server3:/consul/data -v /XiLife/consul/conf/server3:/consul/config -e CONSUL_BIND_INTERFACE='eth0' --privileged=true --name=consul_server_3 consul:1.4.4 agent -server -ui -node=consul_server_3 -client='0.0.0.0' -datacenter=xdp_dc -data-dir /consul/data -config-dir /consul/config -join=$JOIN_IP;
验证1:docker exec consul_server_1 consul operator raft list-peers
验证2:http://192.168.16.170:8500/
2.3 Consul Client实例创建
(1)准备services.json配置文件,向Consul注册两个同样的Product API服务
{
"services": [
{
"id": "core.product-/192.168.16.170:8000",
"name": "core.product",
"tags": [ "xdp-/core.product" ],
"address": "192.168.16.170",
"port": 8000,
"checks": [
{
"name": "core.product.check",
"http": "http://192.168.16.170:8000/api/health",
"interval": "10s",
"timeout": "5s"
}
]
},
{
"id": "core.product-/192.168.16.170:8001",
"name": "core.product",
"tags": [ "xdp-/core.product" ],
"address": "192.168.16.170",
"port": 8001,
"checks": [
{
"name": "core.product.check",
"http": "http://192.168.16.170:8001/api/health",
"interval": "10s",
"timeout": "5s"
}
]
}
]
}
有关配置文件的细节,请移步另一篇文章:《.NET Core微服务之基于Consul实现服务治理(续)》
(2)Consul Client实例
docker run -d -p 8550:8500 --restart=always -v /XiLife/consul/conf/client1:/consul/config -e CONSUL_BIND_INTERFACE='eth0' --name=consul_client_1 consul:1.4.4 agent -node=consul_client_1 -join=$JOIN_IP -client='0.0.0.0' -datacenter=xdp_dc -config-dir /consul/config
(3)验证
2.4 服务检查监控邮件提箱
(1)为Client添加watches.json
{
"watches": [
{
"type": "checks",
"handler_type": "http",
"state": "critical",
"http_handler_config": {
"path": "http://192.168.16.170:6030/api/Notifications/consul",
"method": "POST",
"timeout": "10s",
"header": { "Authorization": [ "token" ] }
}
}
]
}
*.这里的api接口 http://192.168.16.170:6030/api/Notifications/consul是我的一个通知服务接口,下面是实现的代码
/// <summary>
/// 发送Consul服务中心健康检查Email
/// </summary>
[HttpPost("consul")]
public async Task SendConsulHealthCheckEmail()
{
using (var stream = new MemoryStream())
{
HttpContext.Request.Body.CopyTo(stream);
var ary = stream.ToArray();
var str = Encoding.UTF8.GetString(ary); dynamic notifications = JsonConvert.DeserializeObject(str);
if (notifications == null || notifications.Count == )
{
return;
} var title = "XDP服务中心健康检查通知";
var emailBody = new StringBuilder($"<span style='font-weight:bold; color:red;'>{title}</span> : <br/>");
foreach (var notification in notifications)
{
emailBody.AppendLine($"---------------------------------------------------------<br/>");
emailBody.AppendLine($"<span style='font-weight:bold;'>节点</span>:{notification.Node}<br/>");
emailBody.AppendLine($"<span style='font-weight:bold;'>服务ID</span>:{notification.ServiceID}<br/>");
emailBody.AppendLine($"<span style='font-weight:bold;'>服务名称</span>:{notification.ServiceName}<br/>");
emailBody.AppendLine($"<span style='font-weight:bold;'>检查ID</span>:{notification.CheckID}<br/>");
emailBody.AppendLine($"<span style='font-weight:bold;'>检查名称</span>:{notification.Name}<br/>");
emailBody.AppendLine($"<span style='font-weight:bold;'>检查状态</span>:{notification.Status}<br/>");
emailBody.AppendLine($"<span style='font-weight:bold;'>检查输出</span>:{notification.Output}<br/>");
emailBody.AppendLine($"---------------------------------------------------------<br/>");
} var email = new Email()
{
Username = _configuration["EmailSettings:Username"],
Password = _configuration["EmailSettings:Password"],
SmtpServerAddress = _configuration["EmailSettings:SmtpServerAddress"],
SmtpPort = Convert.ToInt32(_configuration["EmailSettings:SmtpPort"]),
Subject = title,
Body = emailBody.ToString(),
Recipients = _configuration["EmailSettings:Recipients"]
}; email.Send();
}
} /// <summary>
/// 使用同步发送邮件
/// </summary>
public void Send()
{
using (SmtpClient smtpClient = GetSmtpClient)
{
using (MailMessage mailMessage = GetClient)
{
if (smtpClient == null || mailMessage == null) return;
Subject = Subject;
Body = Body;
//EnableSsl = false;
smtpClient.Send(mailMessage); //异步发送邮件,如果回调方法中参数不为"true"则表示发送失败
}
}
}
(2)验证
三、Ocelot网关配置
3.1 为Ocelot增加Consul支持
(1)增加Nuget包:Ocelot.Provider.Consul
Nuget>> Install-Package Ocelot.Provider.Consul
(2)修改StartUp.cs,增加Consul支持
s.AddOcelot()
.AddConsul();
更多内容,请移步:Ocelot官方文档-服务发现
3.2 修改Ocelot配置文件增加Consul配置
"GlobalConfiguration": {
"BaseUrl": "http://api.xique.com",
"ServiceDiscoveryProvider": {
"Host": "192.168.16.170",
"Port": 8550,
"Type": "Consul"
}
}
*.这里指向的是Consul Client实例的地址
此外,Ocelot默认策略是每次请求都去Consul中获取服务地址列表,如果想要提高性能,也可以使用PollConsul的策略,即Ocelot自己维护一份列表,然后定期从Consul中获取刷新,就不再是每次请求都去Consul中拿一趟了。例如下面的配置,它告诉Ocelot每2秒钟去Consul中拿一次。
"Type": "PollConsul",
"PollingInterval": 2000
3.3 Service配置
// -- Service
{
"UseServiceDiscovery": true,
"DownstreamPathTemplate": "/api/{url}",
"DownstreamScheme": "http",
"ServiceName": "core.product",
"LoadBalancerOptions": {
"Type": "RoundRobin"
},
"UpstreamPathTemplate": "/product/{url}",
"UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ]
}
这里配置了在Consul中配置的服务名(ServiceName),以及告诉Ocelot我们使用轮询策略(RoundRobin)做负载均衡。
3.4 验证
第一次访问:
第二次访问:
四、HA示例整体架构
对于实际应用中,我们往往会考虑单点问题,因此会借助一些负载均衡技术来做高可用的架构,这里给出一个建议的HA示例的整体架构:
对于一个API请求,首先会经历一个Load Balancer才会到达API Gateway,这个Load Balancer可以是基于硬件的F5,也可以是基于软件的Nginx或LVS再搭配Keepalived,一般来说大部分团队都会选择Nginx。然后API Gateway通过部署多个,来解决单点问题,也达到负载均衡的效果。而对于API Gateway和Consul Client之间的连接,我们往往也会增加一个Load Balancer来实现服务发现的高可用,这个Load Balancer也一般会基于Nginx/LVS搭配Keepalived,API Gateway只需要访问一个Virtual IP即可。而在Consul Data Center中,Consul Server会选择3或5个,Consul Client也会部署多个,刚刚提到的Virtual IP则会指向多个Consul Client,从而防止了Consul Client的单点问题。
最后,祝大家端午安康!
基于Docker的Consul服务发现集群搭建的更多相关文章
- ActiveMQ此例简单介绍基于docker的activemq安装与集群搭建
ActiveMQ拓展连接 此例简单介绍基于Docker的activemq安装与集群搭建 一 :安装 1.获取activemq镜像 docker pull webcenter/activemq 2.启动 ...
- 安装-consul服务发现集群
centos 7.4.x consul 1.2.2 list: 172.16.16.103 172.16.16.112 172.16.16.115 下载: #cd /usr/local/ #wget ...
- 基于docker实现redis高可用集群
基于docker实现redis高可用集群 yls 2019-9-20 简介 基于docker和docker-compose 使用redis集群和sentinel集群,达到redis高可用,为缓存做铺垫 ...
- 小D课堂 - 新版本微服务springcloud+Docker教程_6-06 zuul微服务网关集群搭建
笔记 6.Zuul微服务网关集群搭建 简介:微服务网关Zull集群搭建 1.nginx+lvs+keepalive https://www.cnblogs.com/liuyisai/ ...
- 分布式协调服务Zookeeper集群搭建
分布式协调服务Zookeeper集群搭建 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.安装jdk环境 1>.操作环境 [root@node101.yinzhengjie ...
- 微服务Consul系列之集群搭建
在上一篇中讲解了Consul的安装.部署.基本的使用,使得大家有一个基本的了解,本节开始重点Consul集群搭建,官方推荐3-5台Server,因为在异常处理中,如果出现Leader挂了,只要有超过一 ...
- 基于Kubernetes v1.24.0的集群搭建(二)
上一篇文章主要是介绍了,每台虚拟机的环境配置.接下来我们开始有关K8S的相关部署. 另外补充一下上一篇文章中的K8S的changelog链接: https://github.com/kubernet ...
- 搜索服务Solr集群搭建 使用ZooKeeper作为代理层
上篇文章搭建了zookeeper集群 那好,今天就可以搭建solr搜服服务的集群了,这个和redis 集群不同,是需要zk管理的,作为一个代理层 安装四个tomcat,修改其端口号不能冲突.8080~ ...
- 同主机下Docker+nginx+tomcat负载均衡集群搭建
想用Docker模拟一下nginx+tomcat集群部署,今天折腾了一天,遇坑无数,终于在午夜即将到来之际将整个流程走通,借本文希望给同样遇到类似问题的小伙伴们留点线索. 主机环境是CentOS 7, ...
随机推荐
- docker下MySQL的主从复制
MySql的主从复制 sudo docker pull MySQL:5.7 拉取MySQL的镜像文件(版本号为 5.7) sudo docker run -p 3339:3306 --name mas ...
- django 做 migrate 时 表已存在的处理
在开发web的时候,如果是以前已存在的项目,项目下载下来后,为了使用测试库的数据,会直接将整个测试库(如sqlite3)拿到本机来.这种情况下,如果执行的顺序不对,很容易在执行migrate的时候出现 ...
- PHP Loser 说说做前端需要如何进一步学习
PHP Loser 说说做前端需要如何进一步学习 做前端的,需要如何进一步学习?书籍这个事情贵精不在多,我这里推荐两本即可: <javascript教程 高级程序设计> <CSS权威 ...
- Luogu P2210 Haywire 题解
其实这题吧...有一种玄学解法 这题的要求的就是一个最小化的顺序 那么,我们就不进想到了一种显然的写法 就是random_shuffle 什么?这不是乱搞的非正解吗 然而,正如一句话说的好 一个算法, ...
- jQuery-文件上传问题解决
后端要求文件上传需传参数为二进制流,用form-data方式传递,如下图所示: 为了满足该输入参数要求,上传代码如下: <input type="file" id=" ...
- OAuth2.0授权登录
最近工作中遇到了多系统间的授权登录,对OAuth2.0进行了学习研究,并总结备忘. [场景] 我们登录一些论坛等网站的时候,如果不想单独注册该网站账号,可以选择用微信或QQ账号进行授权登录. 这样的第 ...
- sql server报【将截断字符串或二进制数据】错误
会出现这个错误的原因是因为表设置的列长度小于要插入的数据的长度. 可以从下列的6个方面去排查: 1.表设置的列名长度太短. 2.插入的数据太长. 3.有默认值. 4.有触发器. 5 从char数据类型 ...
- 缓存AJAX的请求
在客户端缓存Ajax请求 浏览器可以缓存图片.js文件.css文件,同样浏览器也可以缓存XML Http调用(当然这需要XML Http以get方式发送调用),这种缓存基于URL,当我们发送一个请 ...
- 为什么局部内部类中访问同一方法中的变量,该变量一定要是final修饰的
最近有一个疑惑:为什么局部内部类中访问同一方法中的变量,该变量一定要是final修饰的 首先,我们看一个局部内部类的例子: class OutClass { ...
- 031.[转] 从类状态看Java多线程安全并发
从类状态看Java多线程安全并发 pphh发布于2018年9月16日 对于Java开发人员来说,i++的并发不安全是人所共知,但是它真的有那么不安全么? 在开发Java代码时,如何能够避免多线程并发出 ...