用.NET Core构建安全的容器化的微服务

微服务热潮正在如火如荼地进行,也有着充分的理由。它不是每个问题的银弹,但它无疑成为企业软件系统中可扩展性和弹性的实用解决方案。

.Net Core项目在微服务领域也取得了一些重大进展,使你能够利用.Net Core Framework中预先编写的代码制作可靠的跨平台应用程序。这使你能够在Windows,OSX或Linux工作站上开发精简的微服务,并将它们部署到Windows,OSX或Linux服务器。生成Linux二进制文件的能力意味着你可以利用此平台上进行容器化。

今天我将展示在.Net Core 2(Web API)中构建REST 微服务并将其部署到Debian服务器的容器中是多么容易。有足够多的文档讲过这个过程的一部分,但这篇是一个全面的教程,展示了从开始到结束的过程。

创建.Net Core项目

我们将使用Dotnet CLI创建我们的应用程序。为此你需要:

  • 安装了.NET Core SDK的计算机(可以是Windows,Mac或Linux)
  • 一个文本编辑器(我使用Visual Studio Code,这是可选的)
  • 测试Web API的方法(我正在使用POSTMan)

提示一下,你可以在Windows中使用Visual Studio使用内置向导创建此项目,结果是一样的。我更喜欢手动创建。

首先,你需要创建项目,我在命令提示符执行以下命令。

dotnet new webapi -o friendlyphonenumber 

这将构建一个新的.Net Core Web API项目。你将拥有你需要包含在/friendlyphonenumber目录中的所有内容。创建工作在所有三个操作系统平台上都是一样的。

设置序列化

打开你的friendlyphonenumber.csproj文件并添加以下内容到你的包引用列表项中:

<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.3.0" />

这会将我们的序列化服务添加到项目中。然后运行

dotnet restore

这样就让你能够控制序列化,特别是以你选择的格式命名属性,而不是遵从C#命名约定。

创建一些模型

这个服务服务使用REST API,我们将向其发送JSON对象。然后它将处理数据并使用Web请求返回一个新对象。尽管这里只有单个属性,但我们将为每个发送和返回的对象创建一个模型。

保存文件。现在我们将为将要为传出电话号码以类似方式格式化而创建一个容器。

创建另一个名为FormattedPhoneNumber.cs的类。为这些对象确认并引用System.Runtime.Serialization包。

using System.Runtime.Serialization;
namespace friendlyphonenumber.Models {
 [DataContract(Name = "FormattedPhoneNumber")]
 public class FormattedPhoneNumber {
  [DataMember(Name = "PhoneNumber")]
  public string PhoneNumber {
   get;
   set;
  }
 }
}

最后,让我们创建这个服务的核心,即电话号码转换器。这接收一个数字电话号码并将其转换为友好的文本。

using System;
namespace friendlyphonenumber.Models {
 public class PhoneNumberFormatter {
  public long NumericPhoneNumber {
   get;
   set;
  }
  public string FormattedPhoneNumber {
   get;
   set;
  }
  public void ConvertPhoneNumber() {
   FormattedPhonenumber = String.Format("{0:(###) ###-####}", NumericPhoneNumber);
  }
 }
}

这是一个不错的可测试对象,如果喜欢我们可以稍后进行扩展。

创建控制器

接下来,我们将创建一个控制器。在这个新项目中,删除controllers文件夹中的ValuesControllers.cs。这是.Net CLI添加的示例而我们不会使用它。

创建一个新类并将其命名为FormatPhoneNumber.cs。

在这个类中,我们将创建一个方法,该方法将接受有一个带有数字电话号码的POST请求,并返回一个包含格式化电话号码的对象。

我们将添加Microsoft.AspNetCore.Mvc和之前创建的模型的引用。

确保该类实现了Controller类的正确功能。

using Microsoft.AspNetCore.Mvc;
using friendlyphonenumber.Models;
namespace friendlyphonenumber.Controllers {
 [Route("api/[controller]")]
 public class FormatPhoneNumber: Controller {
  PhoneNumberFormatter formatter = new PhoneNumberFormatter();
  public IActionResult Post([FromBody] NumericPhoneNumber phoneNumber) {
   if (phoneNumber != null) {
    formatter.NumericPhoneNumber = phoneNumber.PhoneNumber;
    formatter.ConvertPhoneNumber();
    FormattedPhoneNumber friendlyNumber = new FormattedPhoneNumber() {
     PhoneNumber = formatter.FormattedPhoneNumber
    };
    return Ok(friendlyNumber);
   } else {
    return BadRequest();
   }
  }
 }
}

控制器接收带有用数字编号格式化的对象的Post请求,并返回一个包含“友好”或格式化电话号码的对象的IActionResult

保存这些文件,现在我们准备好运行我们的应用程序。

dotnet run 

你应该看到如下的输出:

我们的Web API在http://localhost:5000上运行。所以我们会用Postman做一个快速的烟雾测试。

在Postman中,我们创建一个简单的POST请求,将原始JSON发送到http://localhost:5000/api /FormatPhoneNumber。我们发送的对象如下所示:

{
    "PhoneNumber": "5035551212"
}

服务处理完这个对象后,我们会得到一个很好的格式化结果:

这样现在我们知道我们的服务按预期工作。所以让我们发布一个依赖于框架的应用程序构建:

dotnet publish -f netcoreapp2.0 -c Release 

它所做的是构建一个应用程序,该应用程序将运行在任何支持的目标上,并使用机器中已安装的.Net Core框架。

快速提示:你可以发布一个包含指定目标的自包含的部署,在我们的例子中就是Debian 9。它将发布运行所需的所有东西,包括框架。它可以在没有安装.Net Core Framework的机器上运行。然而这些构建比较大,而且由于我们正在创建一个微服务,我们希望构建一个更小,更精简的容器,我们可以根据需要进行复制。

完成此构建后,我们获得了应用程序的工件:

我将使用scp将文件传输到我的Debian机器上:

 scp -r * <yourusername>@<your host>:/home/<yourusername>/apps/friendlyphonenumber

设置Linux主机

我们现在要将应用程序部署到Linux服务器。为此,你需要:

  • 连接到互联网的Linux服务器(我使用的是Debian 9)
  • 安装好的.NET Core SDK
  • 安装好的Docker

我已经复制了我的项目并安装了.NET Core SDK,因此我应该可以运行该DLL: 看起来它运行良好。

dotnet friendlyphonenumber.dll

但是当我们测试它时,你会很快注意到一些事情。

如果我们检查本地主机的响应,它有输出。但它会抛出一个错误,因为我们没有发送JSON,但我们至少可以看到处理的响应。如果我们尝试从外部访问它:

你可以看到它被阻止,不起作用。这是因为我们的应用程序只在localhost 接口上进行监听。我们还有更多的步骤来处理我们的应用程序。理想情况下,我们应该使用类似Nginx的代理程序作为代理,但这超出了本文的范围,所以我们将设置应用程序直接在外部接口上侦听。在你的项目中打开Program.cs,并将以下内容添加到创建默认构建器方法中:

.UseKestrel(options => {
 options.Listen(IPAddress.Any, 5000);
})

你的BuildWebHost方法现在应该如下所示:

现在,重建项目并转存新的工件。当我们再次运行该文件时:

dotnet friendlyphonenumber.dll

我们现在可以从外部访问服务器了。好吧,我们刚收到来自项目经理的电子邮件,指出需要通过SSL进行保护这个接口。事实证明,这不是听起来那样难的问题。

将SSL添加到我们的服务

现在我们需要生成证书来保护我们的服务,我们将使用Let's Encrypt构建证书,以便我们确保连接的安全。

注意:这些是分布式设置的步骤来让我们在Debian 9上进行加密。如果你已经让我们加密或在你的服务器上安装了证书,则可以跳过此步骤。如果你使用的是其他版本,请参阅设置文档在你的服务器上进行加密

接下来,我们将安装Let's Encrypt,它现在成为Debian 9发行版的一部分: 现在,我们将运行certbot,仅设置证书而不安装到Web服务器:

sudo apt-get install python-certbot-nginx -t stretchsudo certbot certonly

由于我没有安装Web服务器,它询问我如何处理身份验证部分,然后我将选择一个临时Web服务器(独立运行):

填写完信息后,现在我创建了一个证书:

/etc/letsencrypt/live/sandbox.jeremymorgan.com/fullchain.pem

你的路径将根据你使用的URL而有所不同。现在,我们需要将其转换为.pfx以与Kestrel(.Net Core Web服务器)一起使用。于是要做到这一点,我将执行以下操作在应用程序文件夹中生成.pfx证书:

sudo openssl pkcs12 -export -out friendlyphonenumber.pfx \
-inkey /etc/letsencrypt/live/sandbox.jeremymorgan.com/privkey.pem \
-in /etc/letsencrypt/live/sandbox.jeremymorgan.com/cert.pem \
-certfile /etc/letsencrypt/live/sandbox.jeremymorgan.com/chain.pem

同样,对于你的服务器,你需要将“sandbox.jeremymorgan.com”替换为你的路径的域名。

你会被要求输入一个导出密码,创建一个并记下它,因为它一分钟后会被用到。

更改.pfx的所有权,以便你可以使用它:

sudo chown jeremy friendlyphonenumber.pfx

现在我们为该站点生成了一个pfx文件。接下来,我们需要再次修改应用程序以使用SSL连接进行侦听。在program.cs中,将我们之前添加的Kestrel选项替换为以下内容:

options.Listen(IPAddress.Any, 5001, listenOptions => {
 listenOptions.UseHttps("friendlyphonenumber.pfx", "(your password)");
});

所以它现在应该是这样的:

我们必须在我们的.csproj添加另一个包引用:

<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="2.0.1" />

现在重新构建并将应用程序重新部署到Linux主机。

完成此操作后,我们就只能通过安全方式访问端点:

现在我们准备将它放入一个容器中。

构建Docker容器

我们在此服务器上安装并配置了Docker,因此我想为此应用程序构建一个容器。

现在 ,我将为Docker容器创建一个新目录

~/containers/friendlyphonenumbermkdir artifacts

接下来,我将在此目录中创建一个工件文件夹,并将其中的二进制文件和证书复制到其中。

cp -r ../../apps/friendlyphonenumber artifacts/ 

现在我们将在我们的目录中创建docker文件。这将是一个相当简单的配置:

FROM microsoft/aspnetcore:2.0
WORKDIR /app
COPY ./artifacts .
EXPOSE 5001
ENTRYPOINT ["dotnet", "friendlyphonenumber.dll"]

这个文件只是:

  • 从aspnetcore基础映像开始
  • 创建一个工作目录
  • 将我们的工件复制到容器中
  • 打开5001端口
  • 运行应用程序

现在我们有了我们的Docker文件,我们将构建一个映像:

docker build -t friendlyphonenumber1 .

并构建我们的第一个映像。

然后我们来运行它:

docker run -d -p 5001:5001 friendlyphonenumber1:latest

我们将运行这个容器,并将主机上的端口5001映射到5001,然后我们用curl访问它,并再次从外部访问它,但这次在Docker容器中运行:

现在如果我们想或者需要,我们可以为此添加另一个相同的容器:

docker run -d -p 5002:5001 friendlyphonenumber1:latest --name friendlyphonenumber2

这个容器是一样的,但除了我们在5001上收听的端口之外,它还监听端口5002。

实际上,你可以创建一堆这些文件并使用类似Kubernetes的方法来执行负载平衡和容器管理。

这里有很多可能性,你可以轻松扩展此应用程序以使用更多的容器和更多的服务器。

结论

在本文中,我们介绍了使用.Net Core创建一个SSL安全和容器化的微服务。我们从头到尾介绍了这个过程。如果你要为此构建一个生产应用程序,那么你肯定需要一些更好的错误处理,并使用Nginx作为代理,并使用Kubernetes来管理你的容器。这些设置起来非常简单,而.Net Core包使得构建可扩展到云的可靠微服务变得非常简单。

此应用程序的源代码和Docker文件可在此处找到

如果你有任何问题或意见,请随时留下意见。

本文的版权归 ★忆先★ 所有,如需转载请联系作者。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏从流域到海域

《笨办法学Python》 第0课手记

本博客是学习Python的记录手册,本人计算机在读,有C语言的基础,运行环境为 windows 10家庭中文版(64位),使用Windows PowerShel...

1885
来自专栏linux运维学习

linux学习第六十一篇:主动模式和被动模式,添加监控主机,添加自定义模板,处理图形中的乱码,自动发现

主动模式和被动模式 主动或者被动是相对客户端来讲的 被动模式,服务端会主动连接客户端获取监控项目数据,客户端被动地接受连接,并把监控信息传递给服务端 主动模式,...

2875
来自专栏FreeBuf

从NTDS.dit获取密码hash的三种方法

本文我将为大家介绍一些取证工具,这些工具在渗透测试中将会对我们起到很大的帮助。例如当你提取到了大量的主机内部文件时,你可会发现其中包含如NTDS.dit和系统h...

1143
来自专栏数据和云

一篇文章带你读懂 MySQL 和 InnoDB

原文地址 | http://draveness.me/mysql-innodb.html

1115
来自专栏IT笔记

JavaWeb项目架构之Elasticsearch日志处理系统

Elasticsearch (ES)是一个基于 Lucene 的开源搜索引擎,它不但稳定、可靠、快速,而且也具有良好的水平扩展能力,是专门为分布式环境设计的。

6239
来自专栏代码小睿

一句命令快速合并 JS、CSS

  在项目开发环境下,我们会把 JS 代码尽可能模块化,方便管理和修改,这就避免不了会出现一个项目自身 JS 文件数量达到 10 个或者更多。   而项目上线后...

1829
来自专栏张善友的专栏

冗余代码检查工具Simian

微软web2.0开发示例Kobe,重蹈了Oxite的覆辙。Ayende连续发表了五篇高质量的Kobe探讨贴: Kobe – In the nuts & bolt...

2518
来自专栏云计算教程系列

如何在Ubuntu 14.04中使用NodeJS,SailsJS和DustJS构建SPA(单页应用程序)

Node.js®是一个基于Chrome JavaScript运行时的平台,可轻松构建快速,可扩展的网络应用程序。Node.js使用事件驱动的非阻塞I / O模型...

540
来自专栏walterlv - 吕毅的博客

在 Visual Studio 的解决方案资源管理器中隐藏一些文件

2018-07-04 12:30

783
来自专栏北京马哥教育

SQLite这么娇小可爱,不多了解点都不行啊

简介 SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统。它的设计目标是嵌入式的,目前Android和iOS的设备内置的都是SQLite数...

3288

扫码关注云+社区