修复HTTPS升级后出现 Mixed Content: The page at 'https://xxx' was loaded over HTTPS, but requested an insecure frame 'http://xxx'. This request has been blocked; the content must be served over HTTPS. 的问题 - 脆皮鸡 - 博客园

mikel阅读(398)

来源: 修复HTTPS升级后出现 Mixed Content: The page at ‘https://xxx’ was loaded over HTTPS, but requested an insecure frame ‘http://xxx’. This request has been blocked; the content must be served over HTTPS. 的问题 – 脆皮鸡 – 博客园

背景

image

  • 由于需要使用摄像头拍照,需要将原来的http升级到https,通过一顿捣鼓,升级成功。
  • 不过页面加载出现了问题,具体的提示是说:你的页面是在https环境,但是你访问了一个资源(我这里是iframe,也可能是stylesheet等其他资源),而这个资源是在http环境下的,浏览器不给你这样玩。
  • https只能访问https的资源,也因为此修改了接口的baseURL。

解决办法

在nginx 配置中加上 add_header Content-Security-Policy "upgrade-insecure-requests"; 这一条配置即可。

# 让 http 能够自动转发到 https
server {
listen 80;
server_name yourdomain.com;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate yourcrt.pem;
ssl_certificate_key yourkey.pem;
ssl_session_timeout 5m;
location / {
proxy_pass http://127.0.0.1:1234;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# 加上这条即可 👇
add_header Content-Security-Policy “upgrade-insecure-requests”;
}
}

为什么HTTPS和HTTP不能混用?

  1. 混合内容问题:当一个安全的 HTTPS 页面试图加载非安全的 HTTP 内容时,这种情况被称为“混合内容”。浏览器通常会阻止这种行为,因为它降低了整个页面的安全性。
  2. 安全风险:HTTP 内容没有加密,易受中间人攻击。如果在 HTTPS 页面中加载 HTTP 内容,攻击者可能利用这个未加密的内容来攻击整个页面,比如通过注入恶意脚本。
  3. 隐私和完整性:HTTPS 旨在保护用户数据的隐私和完整性。混合内容使得 HTTPS 页面的这些保障部分失效,因为嵌入的 HTTP 内容不受同样的保护。
  4. 用户信任:用户可能信任一个安全的 HTTPS 页面,如果这个页面包含不安全的内容,这可能误导用户,使他们对整个页面的安全性有错误的理解。

如果实在要混用怎么办?

参考这篇文章:如何在 https 的 iframe 里访问 http 页面? | nginx应用实战-3
原理就是在服务器访问http拿到页面,然后包装成https再返回来。
其实最好就是原http页面也升级成https,上面的方法是针对原http不是自己写的没法改的情况下。

.net core 3 web api jwt 一直 401 - VAllen - 博客园

mikel阅读(240)

来源: .net core 3 web api jwt 一直 401 – VAllen – 博客园

最近在给客户开发 Azure DevOps Exension, 该扩展中某个功能需要调用使用 .NET Core 3 写的 Web Api。
在拜读了 Authenticating requests to your service 之后,我给 Web Api 增加了 JWT 认证。

PS: 我没有照抄代码,问题出现了…..问题出现了…..问题出现了…..

Postman 请求该 Web Api, 一直报 401 Unauthorized, 无论我换何种姿势请求, 都是 401 Unauthorized.
心中哪个老火啊, 上 jwt.io 校验了一下,是合法通过的,就是不知道为毛 Postman 不通过.
百思不得其解,度娘了一下, 有个标题引起了我的注意——.net core 3 web api jwt 一直 401
进去看了一眼,恍然大悟!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

答案竟然是:

在 app.UseAuthorization(); 之前加上 app.UseAuthentication(); 就可以了。

心中十万个草泥马在奔腾,因为这两个方法签名的单词太接近了,而且我英语很烂所以对相似的单词都是一眼略过,以为是一样的就没有照搬添加 app.UseAuthentication();
结果乌龙就发生了…

以下内容转载自:https://www.iteye.com/blog/lucky16-2020198

认证 (authentication) 和授权 (authorization) 的区别

以前一直分不清 authentication 和 authorization,其实很简单,举个例子来说:

你要登机,你需要出示你的身份证和机票,身份证是为了证明你张三确实是你张三,这就是 authentication;
而机票是为了证明你张三确实买了票可以上飞机,这就是 authorization。

在 computer science 领域再举个例子:

你要登陆论坛,输入用户名张三,密码1234,密码正确,证明你张三确实是张三,这就是 authentication;
再一check用户张三是个版主,所以有权限加精删别人帖,这就是 authorization。

如何把ASP.NET Core WebApi打造成Mcp Server - yi念之间 - 博客园

mikel阅读(342)

来源: 如何把ASP.NET Core WebApi打造成Mcp Server – yi念之间 – 博客园

前言#

MCP (Model Context Protocol)即模型上下文协议目前不要太火爆了,关于它是什么相信大家已经很熟悉了。目前主流的AI开发框架和AI工具都支持集成MCP,这也正是它的意义所在。毕竟作为一个标准的协议,当然是更多的生态接入进来才会有意义。使用MCP我们可以把Tools调用标准化,这意味着我们可以忽略语言、框架快速把工具融合到不同的模型中去。现在,如何把现有的业务逻辑快速的接入到模型中,成为模型落地很关键的一步,今天我们就借助微软的Semantic KernelMicrosoft.Extensions.AI框架,通过简单的示例展示,如何把现有的ASP NET Core WebApi转换成MCP Server

概念相关#

接下来我们大致介绍一下本文设计到的相关的概念以及涉及到的相关类库

MCP#

MCP是一个开放协议,它为应用程序向 LLM 提供上下文的方式进行了标准化。它的重点是标准化,而不是取代谁。它涉及到几个核心的概念

  • MCP Hosts: 如Claude DesktopIDEAI工具、或者是你开发的AI程序等
  • MCP Clients: 维护与MCP Servers一对一连接的协议客户端
  • MCP Servers: 轻量级程序,通过标准的Model Context Protocol提供特定能力

简单来说就是你写的AI应用就是MCP Hosts,因为MCP是一个协议,所以你需要通过MCP Clients访问MCP ServersMCP Servers提供的就是工具或者一些其他能力。需要说明的是,如果想在AI应用中使用MCP,模型需要支持Function Calling,当然如果你能通过提示词的方式调试出来也是可以的,但是效果肯定不如本身就支持Function Calling

因为MCP是一个开放协议,所以我们可以把原来固定在AI应用里的工具代码单独抽离出来,使用不同的开发语言形成独立的应用,这样这个Tools应用就可以和AI应用隔离,他们可以不是同一种语言,甚至可以在不同的机器上。所以现在很多开源的组件和平台都可以提供自己的MCP Server了。就和没有微服务概念之前我们代码都写到一个项目里,有了微服务之后我们可以把不同的模块形成单独的项目,甚至可以使用不同的开发语言。可以通过HTTP、RPC等多种方式进行通信。

框架#

简单介绍一下本文涉及到的相关框架及地址:

  • Microsoft.Extensions.AI:微软提供的通过.NET实现AIGC操作的开发基础框架,提供了基础对话Function Calling等基础操作,使用简单扩展性强,支持OTEL要测协议监控模型调用情况。目前已适配Ollama、OpenAI、Azure OpenAI等。项目地址https://github.com/dotnet/extensions/tree/main/src/Libraries/Microsoft.Extensions.AI
  • Semantic Kernel:以Microsoft.Extensions.AI为基础(低版本的不是)打造的更强大的AI开发框架,提供了基础对话Function Calling功能的同时,还提供了多模态、RAG、智能体、流程处理等强大的应用级功能,有.NET、Python、Java三种语言版本。项目地址https://github.com/microsoft/semantic-kernel
  • mcpdotnet(modelcontextprotocol/csharp-sdk):原名为mcpdotnet,现在是.NET构建MCP的官方项目,可以使的Microsoft.Extensions.AI和Semantic Kernel快速的适配到MCP。项目地址https://github.com/modelcontextprotocol/csharp-sdk

实现#

整体来说实现的思路也很简单,因为Semantic Kernel支持加载OpenAPI格式的数据加载成它的Plugins,我们可以把Plugins转换成Microsoft.Extensions.AI提供的标准的AIFunction类型,通过mcpdotnet可以把AIFunction标准类型转换成mcpdotnetTools

WebApi#

我们需要新建一个ASP.NET Core WebAPI项目,用来完成查询天气的功能。首先,添加Swagger支持。当然你使用别的库也可以,这里的重点就是可以得到该项目接口的OpenAPI数据信息。

<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />

其次,添加根据IP查询地址信息的功能

<PackageReference Include="IPTools.China" Version="1.6.0" />

因为IPTools使用的是SQLite数据库,所以需要把db加载到项目里。具体使用细节可以查看该库的具体地址https://github.com/stulzq/IPTools

<ItemGroup>
 <None Update="ip2region.db">
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  </None>
</ItemGroup>

接下来实现具体功能的Controller代码

 /// <summary>
 /// 获取城市天气
 /// </summary>
 [ApiController]
 [Route("api/[controller]/[action]")]
 public class WeatherController(IHttpClientFactory _httpClientFactory) : ControllerBase
 {
     /// <summary>
     /// 获取当前时间
     /// </summary>
     /// <returns>当前时间</returns>
     [HttpGet]
     public string GetCurrentDate()
     {
         return DateTime.Now.ToString("MM/dd");
     }

     /// <summary>
     /// 获取当前城市信息
     /// </summary>
     /// <returns>当前城市信息</returns>
     [HttpGet]
     public async Task<IpInfo> GetLocation()
     {
         var httpClient = _httpClientFactory.CreateClient();
         IpData ipInfo = await httpClient.GetFromJsonAsync<IpData>("https://ipinfo.io/json");
         var ipinfo = IpTool.Search(ipInfo!.ip);
         return ipinfo;
     }

     /// <summary>
     /// 获取天气信息
     /// </summary>
     /// <param name="region">省份</param>
     /// <param name="city">城市</param>
     /// <param name="currentDate">日期(格式:月份/日期)</param>
     /// <returns>天气信息</returns>
     [HttpGet]
     public async Task<string> GetCurrentWeather(string region, string city, string currentDate)
     {
         var httpClient = _httpClientFactory.CreateClient();
         WeatherRoot weatherRoot = await httpClient.GetFromJsonAsync<WeatherRoot>($"https://cn.apihz.cn/api/tianqi/tqybmoji15.php?id=88888888&key=88888888&sheng={region!}&place={city!}")!;
         DataItem today = weatherRoot!.data!.FirstOrDefault(i => i.week2 == currentDate)!;
         return $"{today!.week2} {today.week1},天气{today.wea1}{today.wea2}。最高气温{today.wendu1}摄氏度,最低气温{today.wendu2}摄氏度。";
     }
 }

public class IpData
{
    public string ip { get; set; }
    public string city { get; set; }
    public string region { get; set; }
    public string country { get; set; }
    public string loc { get; set; }
    public string org { get; set; }
    public string postal { get; set; }
    public string timezone { get; set; }
    public string readme { get; set; }
}

public class DataItem
{
    public string week1 { get; set; }
    public string week2 { get; set; }
    public string wea1 { get; set; }
    public string wea2 { get; set; }
    public string wendu1 { get; set; }
    public string wendu2 { get; set; }
    public string img1 { get; set; }
    public string img2 { get; set; }
}

public class WeatherRoot
{
    public List<DataItem> data { get; set; }
    public int code { get; set; }
    public string place { get; set; }
}

代码里实现了三个action,分别是获取城市天气、获取当前城市信息、获取天气信息接口。接下来添加项目配置

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Version = "v1",
        Title = "",
        Description = "",
    });

    var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
});
builder.Services.AddHttpClient();

var app = builder.Build();

//使用OpenApi的版本信息
app.UseSwagger(options =>
{
    options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_0;
});
app.UseSwaggerUI(options =>
{
    options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
});

app.UseAuthorization();

app.MapControllers();

app.Run();

完成上面的代码之后,可以运行起来该项目。通过http://项目地址:端口/swagger/v1/swagger.json获取WebApi接口的OpenAPI的数据格式。

MCP Server#

接下来搭建MCP Server项目,来把上面的WebApi项目转换成MCP Server。首先添加MCPSemanticKernel OpenApi涉及到的类库,因为我们需要使用SemanticKernel来把swagger.json加载成Plugins

<ItemGroup>
  <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
  <PackageReference Include="Microsoft.SemanticKernel.Plugins.OpenApi" Version="1.47.0" />
  <PackageReference Include="ModelContextProtocol" Version="0.1.0-preview.11" />
</ItemGroup>

接下来我们来编写具体的代码实现

IKernelBuilder kernelBuilder = Kernel.CreateBuilder();;
Kernel kernel = kernelBuilder.Build();

#pragma warning disable SKEXP0040

//把swagger.json加载成Plugin
//这里也可以是本地路径或者是文件流
await kernel.ImportPluginFromOpenApiAsync(
   pluginName: "city_date_weather",
   uri: new Uri("http://localhost:5021/swagger/v1/swagger.json"),
   executionParameters: new OpenApiFunctionExecutionParameters 
   { 
       EnablePayloadNamespacing = true
   }
 );

#pragma warning restore SKEXP0040

var builder = Host.CreateEmptyApplicationBuilder(settings: null);
builder.Services
    //添加MCP Server
    .AddMcpServer()
    //使用Stdio模式
    .WithStdioServerTransport()
    //把Plugins转换成McpServerTool
    .WithTools(kernel.Plugins);

await builder.Build().RunAsync();


public static class McpServerBuilderExtensions
{
    /// <summary>
    /// 把Plugins转换成McpServerTool
    /// </summary>
    public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, KernelPluginCollection plugins)
    {
        foreach (var plugin in plugins)
        {
            foreach (var function in plugin)
            {
                builder.Services.AddSingleton(services => McpServerTool.Create(function.AsAIFunction()));
            }
        }

        return builder;
    }
}

MCP的传输层协议可以使用stdio(既标准输入输出)sse或者是streamable,甚至是自定义的方式进行通信。其中stdio可以本机进程间通信,sse或者是streamable进行远程通信。它的消息格式,或者理解为数据传输的格式是JSON-RPC 2.0

其中ImportPluginFromOpenApiAsync方法是其中比较关键的点,它是把OpenApi接口信息转换成Kernel Plugins。它通过读取swagger.json里的接口信息的元数据构建成KernelFunction实例,而具体的触发操作则转换成Http调用。具体的实现方式可以通过阅读CreateRestApiFunction方法源码的实现。

再次AsAIFunction方法则是把KernelFunctionFromMethod转换成KernelAIFunction,因为KernelFunctionFromMethod是继承了KernelFunction类,KernelAIFunction则是继承了AIFunction类,所以这个操作是把KernelFunction转换成AIFunction。可以把KernelAIFunction理解成KernelFunction的外观类,它只是包装了KernelFunction的操作,所以触发的时候还是KernelFunctionFromMethod里的操作。具体的实现可以查看 KernelAIFunction类的实现。

几句简单的代码既可以实现一个Mcp Server,虽然上面我们使用的是Uri的方式加载的OpenAPI文档地址,但是它也支持本地文件地址或者文件流的方式。不得不说微软体系下的框架在具体的落地方面做得确实够实用,因为具体的逻辑都是WebApi实现的,Mcp Server只是一个媒介。

MCP Client#

最后实现的是MCP Client是为了验证Mcp Server效果用的,这里可以使用任何框架来实现,需要引入ModelContextProtocol和具体的AI框架,AI框架可以是Microsoft.Extensions.AI,也可以是Semantic Kernel。这里我们使用Microsoft.Extensions.AI,因为它足够简单也足够简洁,引入相关的类库

<ItemGroup>
    <PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.4.3-preview.1.25230.7" />
    <PackageReference Include="ModelContextProtocol" Version="0.1.0-preview.12" />
</ItemGroup>

其中ModelContextProtocol提供了McpClient功能,Microsoft.Extensions.AI提供具体的AI功能集成。具体实现如下所示

//加载McpServer,以为我们构建的是使用Stdio的方式,所以这里直接使用McpServer路径即可
await using IMcpClient mcpClient = await McpClientFactory.CreateAsync(new StdioClientTransport(new()
{
    Name = "city_date_weather",
    Command = "..\\..\\..\\..\\McpServerDemo\\bin\\Debug\\net9.0\\McpServerDemo.exe"
}));

//加载MCP Tools
var tools = await mcpClient.ListToolsAsync();
foreach (AIFunction tool in tools)
{
    Console.WriteLine($"Tool Name: {tool.Name}");
    Console.WriteLine($"Tool Description: {tool.Description}");
    Console.WriteLine();
}

//中文的function calling,国内使用qwen-max系列效果最好
string apiKey = "sk-****";
var chatClient = new ChatClient("qwen-max-2025-01-25", new ApiKeyCredential(apiKey), new OpenAIClientOptions
{
    Endpoint = new Uri("https://dashscope.aliyuncs.com/compatible-mode/v1")
}).AsIChatClient();

IChatClient client = new ChatClientBuilder(chatClient)
    //开启function calling支持
    .UseFunctionInvocation()
    .Build();

//构建Tools
ChatOptions chatOptions = new()
{
    Tools = [.. tools],
};

//创建对话代码
List<Microsoft.Extensions.AI.ChatMessage> chatList = [];

string question = "";
do
{
    Console.Write($"User:");
    question = Console.ReadLine();

    if (string.IsNullOrWhiteSpace(question) || question == "exists")
    {
        break;
    }

    chatList.Add(new Microsoft.Extensions.AI.ChatMessage(ChatRole.User, question));

    Console.Write($"Assistant:");
    StringBuilder sb = new StringBuilder();
    await foreach (var update in client.GetStreamingResponseAsync(chatList, chatOptions))
    {
        if (string.IsNullOrWhiteSpace(update.Text))
        {
            continue;
        }
        sb.Append(update.Text);

        Console.Write(update.Text);
    }

    chatList.Add(new Microsoft.Extensions.AI.ChatMessage(ChatRole.Assistant, sb.ToString()));

    Console.WriteLine();

} while (true);

Console.ReadLine();

上面的代码实现了McpClient接入AI应用

  • 首先,通过McpClient加载McpServer里的工具
  • 其次,把MCP Tools加载到Microsoft.Extensions.AI
  • 最后,在和AI模型对话的时候把Tools转换成function calling。中文的function calling,个人体验下来国内使用qwen-max系列效果最好

其中mcpClient.ListToolsAsync()获取到的是McpClientTool集合,而McpClientTool继承自AIFunction类,具体可查看McpClientTool实现源码。由此可以看出微软封装Microsoft.Extensions.AI基座的重要性,以后更多的框架都可以围绕Microsoft.Extensions.AI进行封装统一操作,这样大大提升了扩展的便捷性。

当然,你也可以使用Semantic Kernel框架进行上面的操作,这里就不过多赘述了,直接上代码

//加载McpServer,以为我们构建的是使用Stdio的方式,所以这里直接使用McpServer路径即可
await using IMcpClient mcpClient = await McpClientFactory.CreateAsync(new StdioClientTransport(new()
{
    Name = "city_date_weather",
    Command = "..\\..\\..\\..\\McpServerDemo\\bin\\Debug\\net9.0\\McpServerDemo.exe"
}));

//加载MCP Tools
var tools = await mcpClient.ListToolsAsync();

using HttpClientHandler handler = new HttpClientHandler
{
    ClientCertificateOptions = ClientCertificateOption.Automatic
};

using HttpClient httpClient = new(handler)
{
    BaseAddress = new Uri("https://dashscope.aliyuncs.com/compatible-mode/v1")
};

#pragma warning disable SKEXP0070
IKernelBuilder kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.AddOpenAIChatCompletion("qwen-max-2025-01-25", "sk-***", httpClient: httpClient);
//把Tools加载成sk的Plugins
kernelBuilder.Plugins.AddFromFunctions("weather", tools.Select(aiFunction => aiFunction.AsKernelFunction()));

Kernel kernel = kernelBuilder.Build();
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();

PromptExecutionSettings promptExecutionSettings = new()
{
    FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};

var history = new ChatHistory();

while (true)
{
    Console.Write($"User:");
    string input = Console.ReadLine();

    if (string.IsNullOrWhiteSpace(input) || input == "exists")
    {
        break;
    }

    history.AddUserMessage(input);
    var chatMessage = await chatCompletionService.GetChatMessageContentAsync(
    history,
    executionSettings: promptExecutionSettings,
    kernel: kernel);

    Console.WriteLine("Assistant:" + chatMessage.Content);

    history.AddAssistantMessage(chatMessage.Content);
}

Console.ReadLine();

因为MCP是一个协议标准,所以MCP Server可以做到一次构建,到处使用。

运行效果#

运行的时候需要先运行起来WebApi项目,然后把McpServer编译成exe文件,然后运行McpClient项目,我们打印出来了可用的Tools列表。在Client项目进行对话,询问当前天气效果如下

感兴趣的如果想运行具体的代码示例,可以查看我上传的代码示例https://github.com/softlgl/McpDemo

总结#

本文演示了如何把ASP.NET Core WebApi打造成Mcp Server,通过讲解基本概念,介绍使用的框架,以及简单的示例展示了这一过程,整体来说是比较简单的。MCP的重点是标准化,而不是取代。如果想在AI应用中使用MCP,模型需要支持Function Calling.我们可以把原来固定在AI应用里的工具代码单独抽离出来,形成独立的应用,这样这个Tools应用就可以和AI应用隔离,形成独立可复用的工具。

现在AI大部分时候确实很好用,但是它也不是银弹。至于它的边界在哪里,只有不断地使用实践。你身边的事情都可以先用AI尝试去做,不断地试探它的能力。AI帮你做完的事情,如果能达到你的预期,你可以看它的实现方式方法,让自己学习到更好的思路。如果是完全依赖AI,而自己不去思考,那真的可能会被AI取代掉。只有你自己不断的进步,才能进一步的探索AI,让它成为你的好工具。

搭建 Docker 私有镜像仓库 - 乔京飞 - 博客园

mikel阅读(404)

来源: 搭建 Docker 私有镜像仓库 – 乔京飞 – 博客园

我们获取镜像,绝大多数情况下,从 Docker 官方仓库或者知名第三方仓库(如阿里云)中获取,但是对于公司内基于自身的业务生成的 Docker 镜像,很有可能涉及到商业利益,肯定不能对外公开,因此需要存放在公司的 Docker 私有镜像仓库中。

有关 Docker 的私有仓库的搭建过程,也非常简单,一般是获取和运行 Docker 官方提供的 Registry 镜像来实现。但是官网没有提供图形化界面,第三方提供了图形化界面 docker-registry-ui 镜像可以使用。本篇博客就展示带有图形化界面的 Docker 私有镜像仓库搭建过程。

DockerRegistry 的官网地址为:https://hub-stage.docker.com/_/registry

 

一、配置 Docker 信任地址

本篇博客基于之前已经安装好 Docker 的虚拟机进行演示,虚拟机的 ip 地址是:192.168.216.128

使用命令 vim /etc/docker/daemon.json 编辑 docker 服务配置文件,内容如下:

{
//这个是之前添加的 docker 阿里云仓库地址(这里隐藏了我个人账号登录阿里云后看到的仓库地址)
“registry-mirrors”: [“https://xxxxxxxxxx.mirror.aliyuncs.com”],
//这个新增加的 docker 私有镜像仓库 web 页面的地址(这里假设 web 使用的是 8050 端口)
“insecure-registries”:[“http://192.168.216.128:8050”]
}

以上内容新增加了即将要搭建的 Docker 私有镜像仓库的 web 图形化界面访问的地址和端口。

添加完成之后,需重新加载 daemon 服务以及重启 docker 服务:

# 重新加载 daemon
systemctl daemon-reload
# 重启 docker
systemctl restart docker

 

二、搭建私有镜像仓库

我个人喜欢把软件安装在 /app 目录中,因此我先在 app 目录下创建一个目录:mkdir /app/registry-ui

然后进入目录:cd /app/registry-ui/ ,然后创建并编辑 docker-compose.yml 文件:vim docker-compose.yml

version: ‘3.2’
services:
registry:
container_name: registry
restart: always
image: registry
volumes:
./registry-data:/var/lib/registry
ports:
7050:5000
networks:
reg_net
registry-ui:
container_name: registry-ui
restart: always
image: joxit/docker-registry-ui:1.5-static
ports:
8050:80
networks:
reg_net
environment:
REGISTRY_TITLE=乔豆豆的私有仓库
REGISTRY_URL=http://registry:5000
CATALOG_ELEMENTS_LIMIT=”1000″
depends_on:
registry
networks:
reg_net:
driver: bridge

对于 docker-compose 程序说,docker-compose.yml 就是它的配置文件,不要更改配置文件的名字。

当前我们已经进入了 /app/registry-ui/ 目录中,在该目录中指定 docker-compose 命令后,就会读取当前目录下的 docker-compose.yml 配置文件,去拉去两个镜像:registry 和 joxit/docker-registry-ui:1.5-static ,然后运行这两个镜像。其中 joxit/docker-registry-ui:1.5-static 镜像配置了 depends_on 为 registry ,因此 joxit/docker-registry-ui:1.5-static 镜像会在 registry 镜像运行后再运行。

对于 docker-compose 编排的多个容器,如果其内部需要互相访问的话,则需要指定服务名称,比如这里的 joxit/docker-registry-ui:1.5-static 镜像启动后的容器需要访问 registry 镜像运行的容器,因为 registry 镜像运行的容器的服务名称是 registry ,内部启动的端口是 5000 ,因此 joxit/docker-registry-ui:1.5-static 启动的容器,可以通过 http://registry:5000 访问 registry 启动的容器。

在 registry 镜像下配置了数据卷的映射:./registry-data:/var/lib/registry ,因为我们运行 docker-compose 命令是在 /app/registry-ui/ 目录下运行,因此后续我们上传的私有 Docker 镜像,就会存放在 /app/registry-ui/registry-data 目录中。

最后,让我们在 /app/registry-ui/ 下运行 docker-compose 命令吧:docker-compose up -d

image

最后通过 docker ps -a 查看启动的两个容器:

image

然后我们就可以打开浏览器输入 http://192.168.216.128:8050 访问搭建好的 docker 私有镜像仓库页面:

image

 

三、向私有仓库上传镜像

使用 docker images 命令查看本地系统中的镜像列表:

image

我们就以之前自己通过 dockerfile 制作的 fileupdown 镜像为例,介绍如何将其上传到私有镜像仓库中。

要想将自己本地机器上的 Docker 镜像推送到刚才搭建的 Docker 私有镜像仓库中,首先需要重新指定 tag 标签。

比如运行命令:docker tag fileupdown:1.0 192.168.216.128:8050/fileupdown:v1 ,可以发现新的 tag 标签必须包含有私有仓库的 web 页面访问地址,这里将版本号修改成了 v1 进行区别。

image

使用 docker images 重新查看镜像列表,能够发现打了新 tag 标签的镜像,并且其与原镜像具有相同的 image id

执行命令推送镜像到私有仓库:docker push 192.168.216.128:8050/fileupdown:v1

image

刷新私有镜像仓库的网址:http://192.168.216.128:8050 就能展示出我们上传的镜像:

image

点击 fileupdown 可以看到该镜像下的版本列表,当前只有一个版本 v1 ,所以只有一条记录:

image

 

四、从私有仓库下载镜像

使用 docker images 命令查看本地系统中的镜像列表:

image

为了方便识别和区分从私有仓库中下载的镜像,我们先将本地中两个 fileupdown 镜像删除掉,由于两个镜像的 image id 相同,因此只能通过镜像名称和 tag 的组合来删除这两个镜像,运行以下命令:

docker rmi fileupdown:1.0 192.168.216.128:8050/fileupdown:v1

image

然后执行以下命令从私有仓库中下载镜像:

docker pull 192.168.216.128:8050/fileupdown:v1

image

 

五、删除私有仓库中的镜像

默认情况下,不提供删除私有仓库镜像的功能,web 图形化界面中也没有提供,如果想要删除的话,需要进行以下操作。

查看当前运行的容器,找到 registry 容器的 id,执行命令 docker ps -a

image

查看到了 registry 的容器 id 为:3550836066ed ,然后执行以下命令进入容器中:

docker exec -it 3550836066ed sh

然后编辑文件:vi /etc/docker/registry/config.yml

version: 0.1
log:
fields:
service: registry
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
## 增加 delete 下的 enabled 配置,启用删除镜像功能
delete:
enabled: true
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3

修改完成保存后,退出容器,重启 registry 的容器:docker restart 3550836066ed

在本篇博客上面【搭建私有仓库】中有关 docker-compose.yml 配置文件中,我们对于 registry 服务暴露出来的端口是 7050,之所以暴露出这个端口,目的也是为了通过命令直接操作私有镜像仓库,我们首先执行以下命令获取要删除的镜像的 sh256 值:

# 访问地址的格式是:http://私有仓库地址:端口号/v2/镜像名称/manifests/镜像的tag
curl -I -XGET –header “Accept:application/vnd.docker.distribution.manifest.v2+json” \
http://192.168.216.128:7050/v2/fileupdown/manifests/v1

获取的结果为:sha256:6f6351900d2ed40826d85dcbbfd6cf3b12b36d42026e9c06f10f86b51096685f

image

当然最简单的获取方式就是,直接从 web 图形化界面中进行复制:

image

然后就可以删除镜像了,如果采用图形化界面删除的话,可以采用 Postman 发送 delete 请求:

http://192.168.216.128:7050/v2/fileupdown/manifests/sha256:6f6351900d2ed40826d85dcbbfd6cf3b12b36d42026e9c06f10f86b51096685f

image

当然也可以采用 linux 命令,如下所示:

# 反斜杠( \ )表示换行,由于命令太长,不方便查看,所以进行了换行
curl -I -X DELETE \
http://192.168.120.107:5000/v2/fileupdown/manifests/\
sha256:4d523adb3c653bab7dfd0326081860b3cba24dc393f69d6731daa513c435ec0c

以上操作完成后,虽然在 web 图形化界面中看不到了,实际上被删除镜像对于空间的占用并没有释放,因此还需要进入容器中执行命令进行空间垃圾回收:

docker exec -it 3550836066ed sh -c ‘ registry garbage-collect /etc/docker/registry/config.yml’

最终 web 界面中展示的效果如下所示:

image

image

如果你想彻底删除的话,就直接进入数据卷映射的目录中进行删除镜像的名称。

例如:本篇博客映射到本地的数据卷目录为 /app/registry-ui/registry-data ,执行以下命令进入镜像存放的目录:

# 进入存放镜像的目录中
cd /app/registry-ui/registry-data/docker/registry/v2/repositories/

然后执行 ll 查看上传到私有仓库的镜像列表,我们发现只有一个:fileupdown

然后执行删除命令,删除 fileupdown 目录即可:rm -rf fileupdown/

image

最后再执行命令,进行一下空间的垃圾回收即可:

docker exec -it 3550836066ed sh -c ‘ registry garbage-collect /etc/docker/registry/config.yml’

最后强烈建议重启一下私有镜像仓库的容器服务:docker restart 3550836066ed

然后我们再看一下 web 图形化界面,发现就比较干净了:

image

一个基于 C# Unity 开发的金庸群侠传 3D 版,直呼牛逼! - 追逐时光者 - 博客园

mikel阅读(242)

来源: 一个基于 C# Unity 开发的金庸群侠传 3D 版,直呼牛逼! – 追逐时光者 – 博客园

前言

大家应该都知道 Unity 游戏引擎是基于 C# 编程语言开发的,今天大姚给大家分享一个基于 C# Unity 开发的金庸群侠传 3D 版,该游戏真的是勾起了一代人的慢慢回忆。

项目介绍

JYX2一个基于 C# Unity 开发的金庸群侠传 3D 重制版是一个回合制战棋战斗开放世界RPG游戏,它是一个非盈利游戏项目,项目目标为重制经典游戏《金庸群侠传》(在线玩DOS原版)并支持后续一系列MOD和二次开发。你可以在PC、MAC或移动手机平台(或其他支持平台)游玩。

Unity介绍

Unity是一款广泛使用的游戏引擎,它支持多种平台,包括PC、移动设备、游戏主机等,并且提供了丰富的工具和资源,使得开发者能够高效地创建高质量的游戏和应用程序。

项目结构示意图

项目开发计划

技术实现简介

游戏画面截图

项目源码地址

更多项目实用功能和特性欢迎前往项目开源地址查看👀,别忘了给项目一个Star支持💖。

优秀项目和框架精选

该项目已收录到C#/.NET/.NET Core优秀项目和框架精选中,关注优秀项目和框架精选能让你及时了解C#、.NET和.NET Core领域的最新动态和最佳实践,提高开发工作效率和质量。坑已挖,欢迎大家踊跃提交PR推荐或自荐(让优秀的项目和框架不被埋没🤞)。

DotNetGuide技术社区

  • DotNetGuide技术社区是一个面向.NET开发者的开源技术社区,旨在为开发者们提供全面的C#/.NET/.NET Core相关学习资料、技术分享和咨询、项目框架推荐、求职和招聘资讯、以及解决问题的平台。
  • 在DotNetGuide技术社区中,开发者们可以分享自己的技术文章、项目经验、学习心得、遇到的疑难技术问题以及解决方案,并且还有机会结识志同道合的开发者。
  • 我们致力于构建一个积极向上、和谐友善的.NET技术交流平台。无论您是初学者还是有丰富经验的开发者,我们都希望能为您提供更多的价值和成长机会。

欢迎加入DotNetGuide技术社区微信交流群👪

DeepWiki:AI驱动、免费且实用的 GitHub 源码阅读与分析神器! - 追逐时光者 - 博客园

mikel阅读(1626)

来源: DeepWiki:AI驱动、免费且实用的 GitHub 源码阅读与分析神器! – 追逐时光者 – 博客园

前言

GitHub 作为全球最大的代码托管平台,汇聚了无数开发者的智慧结晶,为各行各业的技术进步提供了宝贵的资源。然而,面对浩瀚如海的代码库,如何高效地阅读、理解和分析源码,成为了摆在众多开发者面前的一大挑战。今天大姚给大家分享一个 AI 驱动、免费且实用的 GitHub 源码阅读与分析神器:DeepWiki,帮助开发者更加便捷地探索、理解和利用 GitHub 上的源码资源,从而提升开发效率,加速创新进程。

DeepWiki介绍

DeepWiki 是由 Cognition Labs 公司推出的一款专为开发者打造的免费的 AI 驱动 GitHub 源码阅读与分析神器。它结合了最前沿的人工智能技术,旨在帮助开发者更高效地阅读、理解和分析 GitHub 上的源码,从而加速开发进程,提升代码质量(无需注册即可使用)。

DeepWiki核心功能

  • DeepWiki具备强大的代码解析引擎,能够自动识别并提取代码中的关键元素,如函数、类、变量和注释等。
  • 提供直观的代码可视化功能,如类图、函数调用关系图等,帮助开发者以图形化的方式理解代码结构。
  • 内置多种代码分析工具,如代码质量评估、潜在缺陷预测等,帮助开发者发现代码中的潜在问题。
  • 提供代码片段搜索功能,开发者可以根据关键词快速找到相关的代码示例。
  • 等等…

一键生成 Wiki 轻松搞懂开源项目

只需要将对应 GitHub 仓库链接中的 GitHub 替换为 DeepWiki,即可直接访问对应的 DeepWiki 页面。如:https://deepwiki.com/<user>/<repository>

如下所示,我们要阅读 abp 的源码:

直接一键就给我们生成了 abp 源代码的项目详细 Wiki 文档:

Module Classes 模块类:

Module Lifecycle 模块生命周期:

项目模板概述:

向 DeepWiki 提问关于如何使用开源项目

 

Linux环境安装Git(详细图文)_linux安装git-CSDN博客

mikel阅读(209)

来源: Linux环境安装Git(详细图文)_linux安装git-CSDN博客

说明
此文档Linux环境为:Ubuntu 22.04,本文档介绍两种安装方式,一种是服务器能联网的情况通过yum或apt命令下载,第二种采用源码方式安装。

一、yum/apt方式安装
1.yum方式安装Git
如果你的服务器环境是centos/redhot,就可以按照以下方式进行安装Git,只不过这样安装的Git的版本不能确定。

Git 的工作需要调用 curl,zlib,openssl,expat,libiconv 等库的代码,所以需要先安装这些依赖工具。

(1)先下载相关依赖

yum install curl-devel expat-devel gettext-devel \
openssl-devel zlib-devel
(2)下载Git

yum -y install git-core
(3)验证Git

git –version
2.apt方式安装Git
(1)下载相关依赖

apt-get install libcurl4-gnutls-dev libexpat1-dev gettext \
libz-dev libssl-dev
(2)下载Git

apt-get install git
中途如果要输入y/n直接输入y即可。如果弹出什么紫色界面,直接选择OK即可。

(3)验证Git

git –version
二、源码安装Git
1.源码下载地址
官方地址:Git

 

2.依赖准备
还是按照上面提到的使用yum或者apt将相关系统依赖下载。

yum

yum install curl-devel expat-devel gettext-devel \
openssl-devel zlib-devel
apt

apt-get install libcurl4-gnutls-dev libexpat1-dev gettext \
libz-dev libssl-dev
3.解压安装源码包
# 解压
tar -zxf git-2.34.1.tar.gz
# 切换到解压好的目录
cd git-2.34.1
# 编译
make prefix=/usr/local/git all
# 安装
make prefix=/usr/local/git install
4.配置环境变量
vim /etc/profile

在文件底部加上如下所示:

export PATH=$PATH:/usr/local/git/bin
保存退出。

刷新环境变量

source /etc/profile

5.验证Git
git –version

到此,git安装结束。

参考文档:Git 安装配置 | 菜鸟教程
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/m0_52985087/article/details/136205445

Linux 搭建安装Maven环境以及编译打包_使用源码编译mvn package-CSDN博客

mikel阅读(249)

来源: Linux 搭建安装Maven环境以及编译打包_使用源码编译mvn package-CSDN博客

1.前言
这里,已经提前下载并安装好JDK 。如果没安装,先安装JDK。
2. 安装Maven
2.1 下载
# wget http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz
2.2 解压
# tar -zxvf apache-maven-3.3.9-bin.tar.gz
# mv apache-maven-3.3.9 /usr/local/maven3
2.3 配置系统maven环境
# vim /etc/profile
MAVEN_HOME=/usr/local/maven3
export MAVEN_HOME
export PATH=${PATH}:${MAVEN_HOME}/bin
# source /etc/profile
2.4 验证是否成功
# mvn -v
3. 建立maven仓库
3.1 建立一个文件夹保存常用的包
假设路径为: /home/sailing/DAG_web/repo
3.2 配置用户范围settings.xml 文件
# vim /usr/local/maven3/conf/settings.xml
设置仓库路径:
<!– localRepository
   | The path to the local repository maven will use to store artifacts.
   |
   | Default: ${user.home}/.m2/repository
  <localRepository>/path/to/local/repo</localRepository>
  –>
<localRepository>/home/sailing/DAG_web/repo</localRepository>
网络不好,也可以更换镜像源 :
  <mirrors>
    <!– mirror
     | Specifies a repository mirror site to use instead of a given repository. The repository that
     | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
     | for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
     |–>
   <mirror>
  <id>alimaven</id>
  <name>aliyun maven</name>
  <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
  <mirrorOf>central,*</mirrorOf>
</mirror>
<mirror>
<id>repo1</id>
<mirrorOf>repo1</mirrorOf>
<name>maven central</name>
<url>http://repo1.maven.org/maven2/</url>
</mirror>
<mirror>
<id>spring</id>
<mirrorOf>spring-milestones</mirrorOf>
<name>Human Readable Name for this Mirror.</name>
<url>https://repo.spring.io/libs-snapshot/</url>
</mirror>
  </mirrors>
3.3 在终端运行命令将常用的包从maven中央仓库下载文件到本地:
# mvn help:system
打开/home/sailing/DAG_web/repo目录会发现多了很多文件
4. jar包编译打包
Maven最主要的命令:mvn clean compile、mvn clean test、mvn clean package、mvn clean install。执行test之前是会先执行compile的,执行package之前是会先执行test的,而类似地,install之前会执行package。
mvn clean compile:clean告诉Maven清理输出目录target/,compile告诉Maven编译项目主代码
mvn clean test:执行src/test/main下面的test方法,在执行测试之前,会自动执行项目主资源处理、主代码编译、测试资源处理、测试代码编译等工作
mvn clean package:打包,使用默认打包类型jar,在打包前会执行编译、测试等操作。生产的jar文件位于target下面
mvn clean install:将项目输入的jar安装到了Maven本地仓库中
4.1 切换到pom.xml目录下,编译打包
# mvn clean && mvn compile && mvn package       //偷懒,这里一行命令
如果maven项目更新pom.xml后,需要执行mvn update ,本地更新maven项目会自动更新jar包。
————————————————
                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/finghting321/article/details/108596803

项目首页 - LinuxJDK1.8安装包下载:本仓库提供了一个适用于Linux系统的JDK 1.8安装包,方便开发者在Linux环境下快速安装和配置Java开发环境 - GitCode

mikel阅读(279)

来源: 项目首页 – LinuxJDK1.8安装包下载:本仓库提供了一个适用于Linux系统的JDK 1.8安装包,方便开发者在Linux环境下快速安装和配置Java开发环境 – GitCode

简介

本仓库提供了一个适用于Linux系统的JDK 1.8安装包,方便开发者在Linux环境下快速安装和配置Java开发环境。

资源文件

  • 文件名jdk-1.8.tar.gz
  • 描述: 该文件为Linux系统下的JDK 1.8安装包,适用于大多数Linux发行版。

使用说明

  1. 下载文件: 点击仓库中的jdk-1.8.tar.gz文件进行下载。
  2. 解压文件:
    tar -xzvf jdk-1.8.tar.gz
    
  3. 配置环境变量:
    • 打开~/.bashrc~/.zshrc文件,添加以下内容:
      export JAVA_HOME=/path/to/jdk1.8
      export PATH=$JAVA_HOME/bin:$PATH
      
    • 保存并关闭文件,然后执行以下命令使配置生效:
      source ~/.bashrc  # 或 source ~/.zshrc
      
  4. 验证安装:
    java -version
    

    如果显示java version "1.8.x_xxx",则表示安装成功。

注意事项

  • 请确保下载的安装包与您的Linux系统架构(如x86_64)匹配。
  • 在配置环境变量时,请将/path/to/jdk1.8替换为实际的JDK安装路径。

支持与反馈

如果您在使用过程中遇到任何问题或有任何建议,欢迎通过仓库的Issue功能进行反馈。

宝塔中安装的docker目录在哪_mob649e81624618的技术博客_51CTO博客

mikel阅读(363)

来源: 宝塔中安装的docker目录在哪_mob649e81624618的技术博客_51CTO博客

宝塔中安装的 Docker 目录及使用指南

引言

在现代网络应用开发和部署中,Docker 技术的引入大大简化了软件的分发和管理。为了方便用户在 Web 环境中使用 Docker,宝塔面板提供了良好的支持。然而,很多用户在安装了 Docker 后,可能会对其工作目录结构和数据存储位置感到困惑。本文将详细介绍宝塔中 Docker 的安装目录,并提供示例代码,帮助用户更好地理解和使用 Docker。

宝塔面板简介

宝塔面板是一个面向 Web 服务器管理的工具,用户可以通过其图形化界面进行一系列操作,包括网站管理、数据库管理、Docker 容器管理等。在宝塔面板中,Docker 提供了一种便于管理和部署应用的方式。

Docker 的安装目录

在宝塔中,Docker 的安装目录通常位于 /var/lib/docker。这个目录是 Docker 默认存储所有容器、卷、镜像等数据的位置。下面是 Docker 目录的一些基本结构:

/var/lib/docker
├── overlay2
├── containers
├── networks
├── images
├── volumes
└── ...

 

  • overlay2: 存储镜像和文件系统层。
  • containers: 存储容器的文件和元数据。
  • networks: 存储网络配置。
  • images: 存储镜像信息。
  • volumes: 存储数据卷,允许持久化数据。

访问 Docker 目录

要访问 Docker 的安装目录,您可以使用 SSH 连接到服务器,并执行以下命令:

cd /var/lib/docker
ls -la

该命令将列出 Docker 所有的子目录和文件。

Docker 的基本操作

在宝塔面板中,用户可以很方便地创建、启动、停止 Docker 容器。以下是一些基础的 Docker 命令示例:

创建并运行 Docker 容器

我们可以通过以下命令创建并启动一个新的 Docker 容器,以下示例使用 Nginx 镜像:

docker run -d --name mynginx -p 80:80 nginx

查看运行中的容器

要查看当前正在运行的 Docker 容器,可以使用:

docker ps

停止和删除容器

如果需要停止并删除某个容器,可以使用以下命令:

docker stop mynginx
docker rm mynginx

删除镜像

如果需要删除一个不再需要的 Docker 镜像,可以使用:

docker rmi nginx

关系图

为了更好地理解 Docker 容器、镜像和数据卷之间的关系,我们绘制了下面的关系图。

 

CONTAINERstringnamestringiddatetimecreated_atIMAGEstringnamestringiddatetimecreated_atVOLUMEstringnamestringidusesmounts

在这个关系图中,我们看到每个容器都可以使用多个镜像,并且可以挂载多个数据卷。

数据的持久化

在 Docker 容器中,应用的数据可以通过数据卷进行持久化。在宝塔中,使用数据卷可以确保即使容器被删除,数据仍然存在。

创建数据卷

您可以通过以下命令创建一个数据卷:

docker volume create myvolume

在容器中使用数据卷

在运行容器时,可以将数据卷挂载到容器中,例如,将数据卷挂载到 /data 目录:

docker run -d --name myapp -v myvolume:/data myimage

使用宝塔面板管理 Docker

宝塔面板提供了一个友好的界面来管理 Docker。用户可以通过面板轻松创建、管理容器,下面是简单的操作步骤:

  1. 登录宝塔面板。
  2. 在左侧导航栏选择“Docker”。
  3. 点击“创建”按钮,填写容器相关信息。
  4. 点击“确认”创建并运行容器。

通过宝塔面板的图形化界面,用户无需掌握太多命令行操作即可完成对 Docker 的管理。

饼状图示例

以下是 Docker 资源占用情况的饼状图示例,我们假设有多个资源占用情况:

 

40%30%20%10%Docker Resource AllocationContainer UsageImage UsageVolume UsageNetwork Usage

这个饼状图表示了 Docker 各类资源的占用比例,帮助用户更直观地理解资源分布情况。

结尾

通过以上内容,我们了解了宝塔中 Docker 的安装目录、基本操作,以及如何在宝塔面板中管理 Docker。Docker 的使用不仅提高了开发效率,也对应用的部署与维护带来了极大的便利。希望本文能够帮助您更好地理解和使用 Docker,如果您有任何问题或想法,请随时反馈。