闲来无事自己在电脑上搭了一套docker容器化加上服务发现反向代理的套餐,在这儿把流程写个大概,后面玩儿的别踩坑了。
源码地址:https://github.com/Asomnus/MyProject1.git
一、环境工具
1.开发:net8.0 SDK、vs2022(我用的这个,支持8.0都行)、mysql、redis等等(根据业务自己选)
2.虚拟机相关:linux镜像(博主用的ubuntu24.10,不同版本有些微区别),vmware虚拟机工具,windows的WSL(上传文件用)
相关的教程都有,或者自己鼓捣去官网下载都行,VM的话需要科技而且有点麻烦(要登录注册个账号)linux直接清华大学镜像下载,我把VM的百度网盘放下面需要自取
VM百度网盘:通过网盘分享的文件:VMware-workstation-full-17.5.2-23775571.exe
链接: https://pan.baidu.com/s/1ectiNvLWKxy3EceSnpuo3w?pwd=7685 提取码: 7685
Linux镜像下载地址(国内)
阿里云官方镜像站:http://mirrors.aliyun.com
网易开源镜像站:http://mirrors.163.com/
下载教程可以参照:https://blog.csdn.net/weixin_45910254/article/details/131241888
环境搭建流程不再赘述了,接下来直接开整
二、创建.NET项目
打开vs2022创建一个webApi项目,项目自带了WeatherForecastController,我们就以此为基础开始。
1.首先,我们的整个服务器框架是consul服务注册发现、ocelot网关、nginx反向代理,对于.net来讲直接在需要注册进consul的项目下引入Consul、Consul.AspNetCore这两个Nuget包,然后在Program中直接注入Consul相关的东西(为了避免多个服务同时需要使用Consul的情况,我们直接将Consul注入抽象为拓展方法、可以单独建一个项目)
先新增配置文件appsettings.Consul.json
内容如下:
{
"Consul": {
"ConsulIP": "localhost",//Consul服务器IP
"ConsulPort": "8500",//监听端口
"ServiceName": "api-service",//服务名称
"Ip": "localhost",//当前服务节点IP
"Port": "5045",//当前服务节点端口
"Weight": 1//权重
}
}
直接附上代码:
- 拓展方法类:
public static class ConsulCollectionExtension
{
public static IServiceCollection AddConsulService(this IServiceCollection services, IConfiguration configuration)
{
var consulOptions = configuration.GetSection("Consul").Get<ConsulOptions>();
// 通过consul提供的注入方式注册consulClient
services.AddConsul(options => options.Address = new Uri($"http://{consulOptions.ConsulIP}:{consulOptions.ConsulPort}"));
var addres = $"http://{consulOptions.IP}:{consulOptions.Port}/health/health";//健康检查地址
// 通过consul提供的注入方式进行服务注册
var httpCheck = new AgentServiceCheck()
{
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
Interval = TimeSpan.FromSeconds(5),//健康检查时间间隔,或者称为心跳间隔
HTTP = addres,//健康检查地址
Timeout = TimeSpan.FromSeconds(10)
};
// Register service with consul
services.AddConsulServiceRegistration(options =>
{
options.Checks = new[] { httpCheck };
options.ID = Guid.NewGuid().ToString();
options.Name = consulOptions.ServiceName;
options.Address = consulOptions.IP;
options.Port = consulOptions.Port;
options.Meta = new Dictionary<string, string>() { { "Weight", consulOptions.Weight.HasValue ? consulOptions.Weight.Value.ToString() : "1" } };
options.Tags = new[] { $"urlprefix-/{consulOptions.ServiceName}" }; //添加
});
return services;
}
/// <summary>
/// 配置Consul检查服务
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
public static IApplicationBuilder UseConsulCheckService(this IApplicationBuilder app)
{
app.Map("/health", app =>
{
app.Run(async context =>
{
await Task.Run(() => context.Response.StatusCode = 200);
});
});
return app;
}
}
- consul参数类:
public class ConsulOptions
{
/// <summary>
/// 当前应用IP
/// </summary>
public string IP { get; set; }
/// <summary>
/// 当前应用端口
/// </summary>
public int Port { get; set; }
/// <summary>
/// 当前服务名称
/// </summary>
public string ServiceName { get; set; }
/// <summary>
/// Consul集群IP
/// </summary>
public string ConsulIP { get; set; }
/// <summary>
/// Consul集群端口
/// </summary>
public int ConsulPort { get; set; }
/// <summary>
/// 权重
/// </summary>
public int? Weight { get; set; }
}
然后在Program.cs中直接调用方法即可
using MyProject1;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Configuration.AddJsonFile("./appsettings.Consul.json", false, true);
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddConsulService(builder.Configuration);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.UseConsulCheckService();
app.Run();
本地如果需要测试的话直接下载Consul到本地解压然后命令行直接运行consul agent -dev(环境变量Path增加你的consul所在文件夹可以任意文件夹启动consul)启动后浏览器输入localhost:8500即可查看面板,将刚刚的appsettings.Consul.json中相关内容改为正确的IP和端口开启调试即可注册进consul(https不行,么得证书,健康检查过不了,有兴趣的自己去了解)
三、Ocelot网关
Ocelot网关同样需要新建一个控制台应用,我们将其命名为Gateway
首先,安装Ocelot、Ocelot.Provider.Polly、Ocelot.Provider.Consul、Polly这几个包
- 配置文件:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
//ocelot限流
"RateLimitOptions": {
"QuotaExceededMessage": "Too many requests!Please wait a moment!", // 当请求过载被截断时返回的消息,中文会出现乱码
"HttpStatusCode": 503 // 当请求过载被截断时返回的http status
},
"AllowedHosts": "*",
"Routes": [ //这里注意一下版本(旧版本用ReRoutes)
{
"DownstreamPathTemplate": "/rest/api/{controller}/{action}/{id}", //下游路径模板
"DownstreamScheme": "http", //下游方案
//"DownstreamHostAndPorts": [
// {
// "Host": "localhost",
// "Port": "5014"
// }
//], //下游主机和端口
"UpstreamPathTemplate": "/backend/rest/api/{controller}/{action}/{id}", //上游路径模板
"UpstreamHttpMethod": [], //上游请求方法,可以设置特定的 HTTP 方法列表或设置空列表以允许其中任何方法
"ServiceName": "api-backend-service", //请求服务名称
"LoadBalancerOptions": {
"Type": "LeastConnection" //负载均衡算法:目前 Ocelot 有RoundRobin 和LeastConnection算法
},
"UseAuthentication": false,
//限流
"RateLimitOptions": { //限流,限制了单位时间内的访问量
"ClientWhitelist": [], //白名单
"EnableRateLimiting": true,
"Period": "1s", //1s, 5m, 1h, 1d
"PeriodTimespan": 3, //多少秒之后客户端可以重试
"Limit": 1000 //统计时间段内允许的最大请求数量
},
//熔断设置
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3, //允许多少个异常请求
"DurationOfBreak": 5000, // 熔断的时间5s,单位为ms
"TimeoutValue": 5000 //单位ms,如果下游请求的处理时间超过多少则自动将请求设置为超时 默认90秒
}
},
{
"DownstreamPathTemplate": "/rest/api/{controller}/{action}", //下游路径模板
"DownstreamScheme": "http", //下游方案
//"DownstreamHostAndPorts": [
// {
// "Host": "localhost",
// "Port": "5014"
// }
//], //下游主机和端口
"UpstreamPathTemplate": "/backend/rest/api/{controller}/{action}", //上游路径模板
"UpstreamHttpMethod": [], //上游请求方法,可以设置特定的 HTTP 方法列表或设置空列表以允许其中任何方法
"ServiceName": "api-backend-service", //请求服务名称
"LoadBalancerOptions": {
"Type": "LeastConnection" //负载均衡算法:目前 Ocelot 有RoundRobin 和LeastConnection算法
},
"UseAuthentication": false,
//限流
"RateLimitOptions": { //限流,限制了单位时间内的访问量
"ClientWhitelist": [], //白名单
"EnableRateLimiting": true,
"Period": "1s", //1s, 5m, 1h, 1d
"PeriodTimespan": 3, //多少秒之后客户端可以重试
"Limit": 1000 //统计时间段内允许的最大请求数量
},
//熔断设置
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 10, //允许多少个异常请求
"DurationOfBreak": 5000, // 熔断的时间5s,单位为ms
"TimeoutValue": 5000 //单位ms,如果下游请求的处理时间超过多少则自动将请求设置为超时 默认90秒
}
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5015", //进行标头查找和替换以及某些管理配置
"ServiceDiscoveryProvider": {
"Scheme": "http",
"Host": "localhost", //你的Consul的ip地址
"Port": 8500, //你的Consul的端口
"Type": "Consul" //类型
//"ConfigurationKey": "Consul" //指定配置键,键入您的配置
}
}
}
- 新建MyConsulServiceBuilder:
public class MyConsulServiceBuilder : DefaultConsulServiceBuilder
{
public MyConsulServiceBuilder(IHttpContextAccessor contextAccessor, IConsulClientFactory clientFactory, IOcelotLoggerFactory loggerFactory)
: base(contextAccessor, clientFactory, loggerFactory) { }
// I want to use the agent service IP address as the downstream hostname
protected override string GetDownstreamHost(ServiceEntry entry, Node node)
=> entry.Service.Address;
}
- Program.cs代码如下:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOcelot().AddConsul<MyConsulServiceBuilder>().AddPolly();
//builder.Services.AddSingleton<IOcelotCache<CachedResponse>,OcelotCache>();
var app = builder.Build();
await app.UseOcelot();
app.Run();
Ocelot也可以和consul独立开不使用Consul,直接将
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": "5014"
}
], //下游主机和端口
配置为正确的端口Ip即可
一定要注意:
builder.Services.AddOcelot().AddConsul
这个是自定义转发获取下游host的,一定要有!不然大坑,默认会以consul节点名称(-node 不设置即为主机名称)为Host转发,比如你的节点为192.168.1.1:7001,你的consul主机在192.168.1.1.2(主机名为ocelot-consul)他默认会将上游请求转发到http://ocelot-consul:7001,这个很坑,一台机器上是发现不了的(别妄想将-node改为下游ip,这样consul完全失去他的意义了)
OK,代码到这儿基本上就没了,ocelot缓存和鉴权的部分可以自己去拓展,一般中小体量我估计用不着,我们接下来讲服务器的部分
三、虚拟机(Linux)
(IIS很简单,直接发布就行了,docker搞个desk完全跑着玩儿,不讲IIS的。)
我们的demo部署到linux一共需要三台虚拟机(电脑拉闸的一台也行)。添加相应的虚拟机,并将虚拟机网络模式改为桥接模式(分开的IP,模拟真实服务器,sudo ip addr 查看当前IP),不知道怎么改的问问百度,不再赘述了。需要数据库的朋友们直接用Mysql吧,sqlserver放Linux真的捞。
ps:需要复制粘贴的朋友,执行:
sudo apt-get install open-vm-tools-desktop
- 安装相关服务,consul、docker
① consul:
更新和安装curl
sudo apt update
sudo apt upgrade -y
sudo apt install -y curl unzip
下载Consul:
curl -O https://releases.hashicorp.com/consul/1.15.4/consul_1.15.4_linux_amd64.zip
解压并移动到系统路径:
unzip consul_1.15.4_linux_amd64.zip
sudo mv consul /usr/local/bin/
检查安装结果:
consul --version
安装成功后可以自己去修改配置文件之类的,我们这儿就不展开了,直接启动:
consul agent -dev -ui -node=consul-dev -client=0.0.0.0
(client为客户端Ip,如果设为192.168.1.1,那么只有这台服务器能访问)
最后查看consul状态:
sudo apt status consul
② docker:
emmm,这个docker吧,我一个从业两年半的名叫坤坤的朋友告诉我一般这个只会用来运行无状态服务,因为docker的持久化和状态存储很麻烦(我很懒),所以我们只将我们的API容器化
首先还是更新:
sudo apt update
sudo apt upgrade -y
安装依赖:
sudo apt install -y \
apt-transport-https \
ca-certificates \
curl \
software-properties-common
添加秘钥:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
添加源:
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
更新软件源并安装Docker:
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io
检查:
docker --version
启动!
sudo systemctl start docker
sudo systemctl enable docker
注:docker下载可能很慢,等不了的可以自己下载然后传上去
- 发布
将自己的服务(Ocelot和API)发布到Linux上,
这时候我们需要用到rsync这个东西(有很多可视化的工具可以拷贝文件到linux,个人喜好)。在使用rsync前,我们需要开启相应服务器的ssh,使用命令:
sudo apt install openssh-server
ssh使用rsync复制文件需要在windows使用wsl然后映射的目录需要加上/mnt/c(c为盘符)...
① 复制远程(linux)至当前目录 rsync user@url:文件路径 例如:rsync username@ip_address:/home/username/filename
② 复制本地文件至远程rsync filename username@ip_address:/home/username
如果复制的是整个目录 则rsync -r source_dir
记住是使用WSL而不是PowerShell之类的,分隔符为'/'不是''
- 将API容器化
进入到API的Linux目录,添加Dockfile(VS自己也可以右键添加容器支持,或者创建项目勾选支持,但是我用他的有问题,我不理解)内容如下:
# 使用官方 ASP.NET 运行时镜像
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 7002# 暴露的端口
# 将发布的项目文件复制到镜像中
COPY . .
# 设置入口点,运行应用程序
ENTRYPOINT ["dotnet", "yourservice.dll"]
当然,自动化部署肯定不一样,会包含发布运行流程等等,我懒所以直接传上去再容器化。
文件夹里面,右键打开终端,或者直接cd到文件目录,执行:
sudo docker build -t myproject:latest .
myproject:latest是标记和镜像名称,记住后面有个 '.',很容易忽略掉
打包完成后,查看已有镜像:
sudo docker images
启动!
sudo docker run -d -p 7001:yourport --name yourcontainername --urls http://+:yourport
7001是宿主机的端口,也就是外部访问这个端口会转发到docker里面的yourport上
OK,容器跑起来了就自己检查对不对咯,先看看日志:
sudo docker logs containerid
然后访问下你的consulUI看看注册进去没,最后调用相关的接口看看可以访问不就行咯,朋友们一定得敲代码才能记得住,光看很无聊的,多多敲!谢谢观看。