用.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 条评论
登录 后参与评论

相关文章

来自专栏Laoqi's Linux运维专列

Kubernetes 1.8.6 集群部署–Node节点(六)

3025
来自专栏编程

说说SSH、SCP和SFTP的那些事儿

SSH、SCP和SFTP都是SSH软件包的组成部分。 SSH 是 Secure Shell 的缩写,由 IETF 的网络小组(Network Working G...

1.4K5
来自专栏BinarySec

Windows x64上的x86重定向

0x00 背景 搬砖过程中遇到一个很奇怪的现象。写了一个程序利用命令regedit来读取注册表的某项值,出现了一个奇怪的现象:在某些电脑上能读到值,在另一些电脑...

3598
来自专栏CDA数据分析师

Python 探针实现原理

本文将简单讲述一下 Python 探针的实现原理。 同时为了验证这个原理,我们也会一起来实现一个简单的统计指定函数执行时间的探针程序。 探针的实现主要涉及以下几...

2958
来自专栏刘望舒

Android系统启动流程(三)解析SyetemServer进程启动过程

前言 上一篇我们学习了Zygote进程,并且知道Zygote进程启动了SyetemServer进程,那么这一篇我们就来学习Android7.0版本的Syetem...

2076
来自专栏林德熙的博客

VisualStudio 扩展开发 安装 Visual Studio SDK添加菜单增加选项传到商店获取工程所有项目升级 2017

本文主要:如何开发一个 visual Studio 扩展,其实扩展也叫插件。 那么就是如何开发一个 vs插件。 本文也记录了我调试 VisualStudio 半...

9922
来自专栏Java帮帮-微信公众号-技术文章全总结

02.Linux安装

02.Linux安装 Linux 安装 本章节我们将为大家介绍Linux的安装。 本章节以 centos6.4 为例。 centos 下载地址: 可以去官网下载...

55411
来自专栏信安之路

Powershell绕过执行及脚本混淆

为什么需要 powershell ?存在必然合理。微软的服务器操作系统因为缺乏一个强大的 Shell 备受诟病。而与之相对,Linux 的 Shell 可谓丰富...

1530
来自专栏大数据架构师专家

Kubernetes踩坑记---单点集群安装

从今天起,我们开始研究k8s ,之所以叫k8s,是因为Kubernetes这个单词的K和S之间还有8个字母,为了方便书写,就直接用8来代替.国外也会偷懒,这...

1101
来自专栏我的博客

max x Yosemide无法安装jdk8解决

1.下载 好jdk 1.7(1.8) 地址:http://www.oracle.com/technetwork/java/javase/downloads/in...

42213

扫码关注云+社区