.NET Core、nginx 和 Postgres,在树莓派上使用 EF





5.00/5 (29投票s)
在不使用 ASP.NET 的情况下,用 .NET Core 实现一个支持 SSL 的服务器,使用 nginx,并测试 Postgres 和 EF,所有这些都在树莓派上运行
目录
- 引言
- 树莓派入门
- 下一步 - PuTTY 和 WinSCP
- 第一大步:安装 PostgresSQL
- 安装 DotNet Core
- 在树莓派上使用 C# 和 .NET Core 连接到 Postgres
- 裸机服务器
- nginx
- 将服务器作为服务运行
- 修订
- 结论
引言
今年,我决定专注于物联网,主要是树莓派 (rPi) 和 Beaglebone 单板计算机 (SBC)。我称之为我的“个人物联网年”。我的兴趣最初让我开始测试这些设备之一是否可以用作简单的网络服务器(是的,当然可以),但它是否拥有足够的算力来支持 SQL 数据库和“真正的”网络应用程序。作为一个测试案例,我计划使用我几年前为客户开发的一个网站,但为了本文的目的,我只想验证各种技术。最终(不是本文当天),我应该能够确定使用非常低成本的 SBC 作为满足低带宽需求的可行网络服务器的可行性。在此过程中,我也可能会玩一些硬件功能。
对于我的主要任务,我计划使用 PostgresSQL(我个人对 MySQL 有历史性的厌恶)和 DotNet Core 作为网络服务器。最初面临三个挑战
- 将 SQL Server 数据库导入 Postgres(SQL Server 的 Docker 镜像不可行,它需要 2GB RAM,而 rPi 只有 1GB RAM,更大的问题是它只支持 x64 处理器,不支持 ARM 处理器)。这里有一个 Postgres 的 Entity Framework Core 提供程序:链接
- 强制 .NET Core 使用我的服务器代码,因为我为客户编写的网站不使用 ASP.NET。
- 搞清楚 SSL 证书在 Linux 环境下如何与 .NET Core 配合使用。
- 步骤 3 涉及配置反向代理服务器,我将使用 nginx 进行演示。
所以会有一些与 SBCs 无关的挑战,也有一些肯定相关的挑战!
树莓派入门
第一步是组装 KanaKit 树莓派并启动它。之后,我想能够从 USB 驱动器启动操作系统,而不是使用 micro SD 卡。
开箱、组装和加载操作系统
打开大盒子,我们发现里面有树莓派本身、电源、HDMI 线、SD 卡、外壳和散热片。
拆开那些盒子,我们得到了实际的硬件!
将树莓派组装到外壳中花了整整 10 或 15 分钟与外壳搏斗(我拒绝看视频!)。结果是你必须将树莓派滑入到位,以便 SD 卡槽正确地定位在外壳的访问孔中。最终,它组装好了(包括散热片)
SD 卡预装了 NOOBS,“开箱即用新软件”,它允许您选择要安装的操作系统。
当我尝试安装推荐的 Raspbian 操作系统(第一个复选框)时,安装过程卡住了(我确实等了几分钟)。我忐忑不安地拔掉电源并重新启动了树莓派,它愉快地重新启动到 NOOBS。选择第二个选项 Raspbian Full,安装没有任何问题。太棒了!
查看硬件和功能
这个版本的树莓派一个不错的特点是内置 WiFi,配置起来非常简单。我从来没有在 BeagleBone Black 上成功让 WiFi 工作过,而 WiFi 是一个要求,因为我希望能够在不局限于我办公室(我有网线连接)的情况下进行这个项目(我的办公室已经堆满了为另一个客户进行的 BeagleBone 项目)。
哪个版本的树莓派?
KanaKit(至少据我所知)并没有告诉我我的树莓派版本,所以经过一番挖掘,我找到了 这个网站 列出了不同的版本。打开命令行窗口并输入
cat /proc/cpuinfo
我确定报告的“版本”是“a020d3”,并在网站(上面链接)上查找后,我确认我拥有一个树莓派 3B+
它支持从 USB 启动吗?
经过大量的谷歌搜索,我发现 3A+ 和 3B+ 树莓派无需任何配置更改即可从 USB 启动。虽然配置更改看起来很简单,但我决定根据这些说明验证树莓派是否支持 USB 启动。结果是,如果你输入
vcgencmd otp_dump | grep 17:
如果返回代码是 0x3020000a
,则表示 OTP(一次性可编程)位已编程为从 USB 启动。这个位表示尝试从 SD 卡启动,如果不起作用,则扫描 USB 设备以获取启动代码。显然,考虑到这是“一次性可编程”的,您仍然可以通过 一点点技巧 更改为从 SD 卡启动。
将操作系统映像写入 USB 驱动器
下一步是将操作系统映像写入 USB 驱动器。我手头已经有一个 2TB 的机械硬盘(不是固态硬盘!),考虑到树莓派不支持 USB 3,使用机械硬盘而不是固态硬盘的性能损失似乎无关紧要,至少在游戏的这个阶段是这样——而且,我没有任何未使用的固态硬盘,我想继续推进项目。
- 从 树莓派下载网站,我选择了“Raspbian Stretch with desktop and recommended software”并下载了它。
- 从 7Zip 网站,我下载并安装了 7Zip,然后解压缩了步骤 1 中的文件,因此我现在有了“2018-11-13-raspbian-stretch-full.img”文件。
- 然后我下载并安装了 Etcher,这是一个简单的三步过程,用于将 .img 文件映像到驱动器。
Etcher UI
将驱动器映像完毕并取出 SD 卡后,树莓派从 USB 驱动器启动了!
橘子、香薰蜡烛(橘子后面)和 特色巧克力(橘子后面的袋子)只有在需要其他咒语才能让事情顺利进行时才需要。
优雅关机
尽可能地,始终使用以下命令优雅地关闭您的树莓派:
sudo shutdown -h now
重启树莓派
在命令行或终端窗口中,输入
sudo reboot
下一步 - PuTTY 和 WinSCP
最终,我不想把显示器、键盘和鼠标连接到树莓派(因此,它也不需要桌面 UI)。相反,与树莓派的交互将使用 PuTTY(telnet 客户端)和 WinSCP 进行文件传输。
启用 SSH
Debian 操作系统内置了 SSH 服务器,但我们需要启用 SSH。启用 SSH 的一种方法是输入
sudo raspi-config
这会弹出一个简单的 UI
- 导航到项目 5 "Interface Options"。
- 选择“P2 SSH - Enable/Disable remote command line access to your Pi using SSH”。
- 当系统提示“Would you like the SSH server to be enabled?”时,选择“Yes”。
- 选择“OK”。
- 选择“Finish”退出配置应用程序。
阅读更多关于 raspi-config 应用程序的信息 在这里。
现在我们可以直接 telnet 到树莓派,一旦我们知道 IP 地址,IP 地址可以通过使用以下命令确定
ifconfig
在控制台窗口中。由于我使用 WiFi,我在 wlan0
部分找到 IP 地址为 inet 192.168.0.15
确定 IP 地址的其他选项包括使用 hostname -I
,它会显示网络 IP、本地 IP 和 IPv6 地址。
PuTTY
启动 PuTTY 并输入您的树莓派的 IP 地址
我觉得保存 IP 地址很有用。
首次连接时(或树莓派的 IP 地址更改时),您会看到如下对话框
点击“是”并继续登录
用户名是“pi”(除非您更改过),密码是您第一次启动操作系统时设置的密码。
如果您想自动登录,可以使用(当然要将 IP 地址更改为您的树莓派!)命令行中的:putty pi@192.168.0.15 -pw [您的密码]
或创建桌面快捷方式。作为给读者的练习(哈哈哈),更好的方法是使用密钥对。
WinSCP
WinSCP(SCP 代表安全复制协议)的设置基本相同,只是在这里您可以输入用户名和密码,并可以选择保存两者
当您将会话保存为站点时,您可以选择也保存密码。
登录后,您会在左侧看到 Windows 目录结构,右侧看到树莓派的操作系统目录结构。这使得在两者之间复制和粘贴文件,甚至使用简单的文本编辑器编辑文件都非常容易。
WinSCP 首次连接时,也会显示安全警告
选择“是”。
第一大步:安装 PostgresSQL
第一大步是安装 PostgresSQL。幸运的是,有一篇 很棒的文章 告诉我如何做到这一点,否则我将永远无法弄清楚任何事情。在 PuTTY 终端(或您树莓派自己的终端窗口中),输入
sudo apt install postgresql libpq-dev postgresql-client postgresql-client-common -y
提示:如果您使用的是 PuTTY,右键单击鼠标可将文本从剪贴板复制到终端窗口。如果您想将文本从终端窗口复制到剪贴板,请用鼠标选择文本并左键单击。
此时,只需按照其余步骤操作,但不包括“现在使用 shell 连接到 Postgres 并创建一个测试数据库。”我执行的步骤是
sudo su postgres
createuser pi -P --interactive
并按照文章中或我操作的屏幕截图所示响应提示(显然,输入您自己的密码)
提示:一旦以超级用户 (“su”) 身份登录,您可以输入 exit
来退出该用户并返回到 “pi” 用户。但是,如果您正在按照本节中的步骤操作,请暂时不要这样做。
远程连接到 Postgres
我们都想远程工作,对吧?我的意思是,在这个时代,有了所有这些技术,开车去办公室似乎很荒谬。抱歉,离题了。继续教程,让我们启用 Postgres,以便我们可以远程连接到它。根据我上面链接的文章
- 编辑 PostgreSQL 配置文件 /etc/postgresql/9.6/main/postgresql.conf,取消注释
listen_addresses
行,并将其值从localhost
更改为*
。使用
nano
,一个简单的终端编辑器,按照步骤 1 中的指示编辑该行。我们以超级用户身份使用 nano,因此我们拥有保存文件的权限。postgres@raspberrypi:/home/pi$ nano /etc/postgresql/9.6/main/postgresql.conf
提示:当前路径位于
$
的左侧,您输入的内容位于$
的右侧。别忘了删除行首的
#
! - 编辑 /etc/postgresql/9.6/main/pg_hba 配置文件,将 IPv4 的
127.0.0.1/32
改为0.0.0.0/0
,将 IPv6 的::1/128
改为::/0
。postgres@raspberrypi:/home/pi$ nano /etc/postgresql/9.6/main/pg_hba.conf
确保编辑那些未被注释掉的行!
关于第三步,重启 postgres 服务,我无法让这一步工作——它一直要求 postgres 用户的密码,但实际上没有。重启树莓派后,我再次尝试该命令,它说服务没有启动!所以我不清楚发生了什么。
在您的 Windows 电脑上安装 pgadmin
从这里下载 pgadmin 3(非常重要的是,您要下载不再支持的 pgadmin 3,因为它是一个桌面应用程序,而不是一个 localhost Web 应用程序,呸)并在您的 Windows 电脑上安装。当然您可以使用 pgadmin4,但我个人更喜欢有一个不需要在浏览器中运行的应用程序。无论如何,用户界面是相似的。使用 pgadmin3,创建与您的树莓派上的 Postgres 的连接(相应地更改 IP 地址和密码)
如果您正确修改了上述文件(我第一次没有),您应该能够连接并浏览数据库
不幸的是,我发现 pgadmin 3 有 bug(当我尝试添加表等时,它会报告查询错误,但这似乎并没有阻止操作完成),而 pgadmin 4(浏览器版本)工作得好得多。这是 pgadmin 4 的屏幕截图
请注意,当我第一次运行 pgadmin 4 时,它无法启动“pgadmin 4 服务器”。当我第二次尝试时,它成功了。去吧。关键是,我们可以连接到运行在树莓派上的 Postgres 服务器!
我们的树莓派表现如何?
让我们使用 free -h
来查看内存分配情况
32M 的共享内存用于显示驱动器。鉴于有 521M 可用,安装 Postgres 后我们的表现相当不错。
使用 df -h
查看磁盘可用空间情况
提示:“-h
”告诉这两个命令将显示量缩短为字节、K(千字节)、M(兆字节)、G(千兆字节)或 T(太字节),精度为 1 位(小数点后),这使得显示更具可读性。
安装 DotNet Core
再一次,“我什么都不知道!”并依靠别人告诉我如何做事。“Dave the Engineer”在 一篇很棒的 MSDN 博客文章 中介绍了如何在树莓派上设置 .NET Core 运行时。但这适用于 .NET Core 2.0,我想安装最新的稳定版本 2.2(我很想安装 3.0,但我会等)。更多的谷歌搜索找到了 Scott Hanselman 的文章,关于安装 .NET Core 2.1。跳过所有 Docker 的内容,直接到文章底部,找到“第二”部分。我特别不想使用 Docker,因为它增加了复杂性,正如 Scott 所写,我想在“裸机”上使用 .NET Core。有趣的是,Scott 的文章包括安装 SDK,据我所读,这是不受支持的。Dejan Stojanovic 有一篇文章 介绍了如何安装 2.2!如果你认为轨道上的太空垃圾是个问题,那么在互联网上找到有关最新技术的文章正变得越来越麻烦!
跳过文章中间部分,我们应该运行这个
wget https://download.visualstudio.microsoft.com/download/pr/
36bff52b-2cdd-4011-8e92-d00f7537704f/9885ba267b1ef29a1401adc387d9a016/
dotnet-sdk-2.2.101-linux-arm.tar.gz
天哪。这些疯狂的数字你是怎么弄清楚的?令人惊讶的是,它成功了
按照他的帖子,我们现在运行
sudo mkdir -p /bin/dotnet && sudo tar zxf dotnet-sdk-2.2.101-linux-arm.tar.gz -C /bin/dotnet
export DOTNET_ROOT=/bin/dotnet
export PATH=$PATH:/bin/dotnet
并编辑 bash.rc 文件
sudo nano ~/.bashrc
并在末尾添加这行
export PATH=$PATH:/bin/dotnet
哇,成功了!(当所有这些东西在 Linux 中工作时,我总是感到惊讶。)
测试 .NET Core 应用程序
按照这里的说明
- 在您的 PC(不是树莓派)上的命令行窗口中,为测试应用程序创建一个目录并
cd
进入该目录。 - 输入:
dotnet new console
- 输入:
dotnet publish -r linux-arm
那么
- 深入文件夹结构以找到 publish 文件夹 - 使用 WinSCP,将此文件夹中的“所有内容”复制到您树莓派上的一个文件夹中。
- 在 PuTTY 窗口中,
cd
进入在树莓派上创建的文件夹,并输入:chmod 755 ./consoleApp
,使该文件成为可执行文件。 - 输入:
./consoleApp
如果一切顺利,您应该会看到“Hello World”输出
万众欢腾!!!
在树莓派上使用 C# 和 .NET Core 连接到 Postgres
现在让我们看看是否可以通过在树莓派上运行的应用程序连接到 Postgres 数据库。我们应该能够在 Visual Studio 中测试代码,连接到树莓派上的 Postgres——哈,还有一个辅助应用程序,使用树莓派作为数据库服务器!
创建一个用于测试的数据库
在我们开始 C# 部分之前,让我们先创建一个数据库和一张简单的表用于测试。在 pgadmin SQL 窗口中,执行以下操作
CREATE TABLE public."TestTable"
(
"ID" serial primary key NOT NULL,
"FirstName" text COLLATE pg_catalog."default",
"LastName" text COLLATE pg_catalog."default"
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE public."TestTable" OWNER to pi;
提示:在 pgadmin 3 中,选择 Tools -> Query Tool,在 pgadmin 4 中,点击 SQL 图标。
提示:serial
(或 bigserial
)关键字是 Postgres 指定自增字段的方式。
添加 Nuget 包
使用上面创建的“consoleApp
”项目,右键单击“Dependencies”
并添加两个依赖项
Microsoft.EntityFrameworkCore
Npgsql.EntityFrameworkCore.PostgreSQL
您的项目现在应该在 NuGet 子文件夹中引用这些内容。
附加命名空间
添加以下命名空间
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
using Microsoft.EntityFrameworkCore;
Diagnostics 将用于计时数据库操作。
TestTable 模型和 DbContext
public class TestTable
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Context : DbContext
{
public DbSet<TestTable> TestTable { get; set; }
public Context(DbContextOptions options) : base(options) { }
}
Main
Main 已修改为执行 TestPostgres
,我们将在下一步看到它。
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
TestPostgres();
Console.ReadLine();
}
TestPostgres 方法
这是一个 async void
方法,通常应避免使用!如果未找到数据,此方法将自动插入几行并计时操作。在下面的代码中,将 [您的密码]
替换为您创建 Postgres pi
用户时使用的密码。
static async void TestPostgres()
{
var contextBuilder = new DbContextOptionsBuilder();
// Database name is case-sensitive
contextBuilder.UseNpgsql
("Host=192.168.0.15;Database=Test;Username=pi;Password=[your password]");
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
using (var context = new Context(contextBuilder.Options))
{
Console.WriteLine(stopwatch.ElapsedMilliseconds + "ms");
stopwatch.Restart();
var items = await context.TestTable.ToListAsync();
Console.WriteLine("First query: " + stopwatch.ElapsedMilliseconds + "ms");
stopwatch.Restart();
Console.WriteLine("Number of items: " + items.Count);
if (items.Count == 0)
{
TestTable t1 = new TestTable() { FirstName = "Marc", LastName = "Clifton" };
TestTable t2 = new TestTable() { FirstName = "Kelli", LastName = "Wagers" };
context.Add(t1);
context.Add(t2);
context.SaveChanges();
Console.WriteLine("Insert: " + stopwatch.ElapsedMilliseconds + "ms");
Console.WriteLine("t1 ID = " + t1.ID);
Console.WriteLine("t2 ID = " + t2.ID);
stopwatch.Restart();
}
else
{
items.ForEach(t => Console.WriteLine
("ID: " + t.ID + " FirstName: " + t.FirstName + " LastName " + t.LastName));
}
// Query again to see how long a second query takes.
var items2 = await context.TestTable.ToListAsync();
Console.WriteLine("Second query: " + stopwatch.ElapsedMilliseconds + "ms");
stopwatch.Restart();
}
using (var context = new Context(contextBuilder.Options))
{
// Query again to see how long a second query takes.
var items2 = await context.TestTable.ToListAsync();
Console.WriteLine("Third query: " + stopwatch.ElapsedMilliseconds + "ms");
}
}
在 Visual Studio 中运行测试
运行程序,您应该会看到类似这样的内容
请注意,第一个查询,即建立到数据库的连接,耗时近 1.5 秒。我曾见过高达 4 秒的情况。
在树莓派上运行测试
别忘了再次运行 publish
命令
dotnet publish -r linux-arm
像之前一样使用 WinSCP,将“所有内容”复制到树莓派。
提示:通常我们不需要复制所有内容,只需复制最新的更改,但由于我们添加了几个 NuGet 包,最好复制整个工具包和随附文件,因为可能会有一些 dll 和 so 文件是按发布日期进行时间戳的,如果您按降序日期排序文件夹内容,它们将不会显示。
提示:有趣的是,一旦 consoleApp
被 chmod
更改为可执行文件,我们就不必再做一次了。
删除在 Visual Studio 运行中创建的测试数据后,您应该会看到类似这样的内容
提示:按 回车 键退出程序,因为它停在 Console.ReadLine()
调用处。
请注意,树莓派连接到 Postgres 需要惊人的 8 秒。幸运的是,一旦连接建立,查询在 100 毫秒内运行,但插入操作需要一秒多。
再次运行测试程序,我们得到的是数据
性能提升?
我尝试强制使用涡轮模式(这会使您的树莓派保修失效!),如 这篇说明 所述,但未发现性能提升。我没有尝试超频选项,因为那可能会导致树莓派无法启动,而且我不想费心恢复 /boot/config.txt 文件,那是设置配置选项的地方。
裸机服务器
GitHub 上已经有 一个优秀的服务器,并且它已经支持 HTTPS,那为什么还要重复造轮子呢?因为我的好奇心把我带到了编程的奇特角落,我想看看(并学习)如何使用 .NET Core 编写一个不基于 ASP.NET Core 堆栈的服务器。我曾经使用 ASP.NET Core 堆栈编写了一个简单的服务,我发现它易于使用,依赖注入(DI)很酷,从 HTTP 头、JSON POST 正文等自动提取参数都非常时髦。绝对推荐!但是,我仍然想尝试一个裸机实现,原因无他,就是我喜欢这样做。所以如果你像我一样好奇,请继续阅读。
HTTP 服务器
HTTP 服务器很简单。首先,让我们修改 Main
来启动一个 HttpListener
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
// TestPostgres();
StartServer();
Console.WriteLine("Press ENTER to exit.");
Console.ReadLine();
}
我们将启动服务器并用 GET
路径响应,同时将动词和路径写入控制台(请记住您树莓派的 IP 地址可能不同)
static void StartServer()
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://192.168.0.15:5000/");
listener.Start();
Task.Run(() => WaitForConnection(listener));
}
static void WaitForConnection(object objListener)
{
HttpListener listener = (HttpListener)objListener;
while (true)
{
HttpListenerContext context = listener.GetContext();
string verb = context.Request.HttpMethod;
string path = context.Request.RawUrl.LeftOf("?").RightOf("/");
Console.WriteLine($"Verb: {verb} Path: {path}");
byte[] buffer = Encoding.UTF8.GetBytes(path);
context.Response.StatusCode = (int)HttpStatusCode.OK;
context.Response.ContentLength64 = buffer.Length;
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
context.Response.Close();
}
}
在文件顶部添加
using System.Net;
using System.Text;
为了完整性,我到处使用的两个扩展方法是
public static class ExtensionMethods
{
public static string LeftOf(this String src, string s)
{
string ret = src;
int idx = src.IndexOf(s);
if (idx != -1)
{
ret = src.Substring(0, idx);
}
return ret;
}
public static string RightOf(this String src, string s)
{
string ret = String.Empty;
int idx = src.IndexOf(s);
if (idx != -1)
{
ret = src.Substring(idx + s.Length);
}
return ret;
}
}
发布、WinSCP 和测试
太棒了!从那里开始,不安全的 HTTP 世界就是我们的游乐场!
幕后 - htop
使用 Debian 附带的交互式进程查看器 htop
很有趣,可以查看运行 Web 服务器应用程序的 .NET Core 启动了多少个进程(有 10 个 ./consoleApp 进程)
nginx
然而,实现 HTTPS 服务器需要一个反向代理服务器,例如 nginx 或 Apache,它将接收传入的 IP 并将其路由到指定的 localhost 端口,然后将响应返回给发出请求的客户端。在深入研究 HTTPS 和 SSL 证书之前,让我们先让一个基本的反向代理与我们的 C# 代码一起工作。
安装和测试 nginx
要在树莓派上安装 nginx,请在终端窗口中输入此命令(这是我从 树莓派网站 学到的)
sudo apt-get install nginx
用以下命令启动服务器
sudo /etc/init.d/nginx start
现在导航到您的树莓派的 IP 地址,您应该会看到 nginx 欢迎界面
这个站点是如何服务的?
因为我对 nginx 一无所知,所以我将探索一些基础知识。显然,nginx 在某个地方创建了一个默认网站和网页。配置文件在 /etc/nginx/sites-available 中,我们看到一个名为 default 的文件
如果我们使用 WinSCP 检查此文件(或者如果您在终端中,使用 nano /etc/nginx/sites-available/default 或 cat /etc/nginx/sites-available/default),我们会看到一些重要的配置行(以下内容已从实际文件中截断)
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
太棒了,所以它正在从 /var/www/html 提供页面,我们在这里看到索引文件
所以现在,我们知道默认网站在哪里提供静态内容了。
配置反向代理以运行 .NET Core ConsoleApp
接下来,我希望将所有非静态内容路由到我的服务器。阅读 nginx 新手指南,我将把 try_files
命令更改为 proxy_pass
并指定 consoleApp
程序监听的 URL。这必须以超级用户身份完成,所以我们使用以下命令行:sudo nano /etc/nginx/sites-available/default 来编辑文件,注释掉 try_files
命令并添加 proxy_pass http://127.0.0.1:5000/;
保存文件后,使用以下命令重启 nginx
sudo /etc/init.d/nginx reload
提示:如果重新加载 nginx 时出现错误,请回到编辑器并撤销您的编辑,保存文件,然后查看重新加载是否有效。然后再次尝试更改文件。我不知道我做了什么,但我第一次进行上述文件更改时,出现了一个错误(没有费心按照说明查看错误是什么),所以我还原了更改并重新应用了它们,然后突然没有错误了!
提示:使用 https://:5000/
不起作用 - 您会收到 nginx 的“未找到”错误,您必须使用 <a href="http://127.0.0.1:5000/">http://127.0.0.1:5000/</a>
。
最后,将 consoleApp
服务器监听的 IP 地址更改为 127.0.0.1
listener.Prefixes.Add("http://127.0.0.1:5000/");
发布,通过 WinSCP 传输更改后的文件,启动 consoleApp
,当您尝试截图中所示的 URL 时,您应该会得到
并在终端窗口中
提示:我在这之前犯了一些错误,一个 非常有用的 Digital Ocean 页面 指导我使用了这个命令
sudo tail -30 /var/log/nginx/error.log
它显示了 nginx 报告的最后 30 行错误。在找出哪里出了问题时非常有用!
提示:自动启动 nginx(来自 此处)
开机自动启动 NGINX
cd /etc/init.d
sudo update-rc.d nginx defaults
移除自动启动
sudo update-rc.d -f nginx remove
HTTPS
反向代理的一个优点是它为您处理 HTTPS——您的本地服务器可以是 HTTP。此外,nginx 支持服务器名称识别 (SNI),这意味着我可以在同一个物理设备和 IP 地址上运行多个域,每个域都有不同的 SSL 证书。目前,我们只为测试创建一个自签名证书。首先,让我们证明 HTTPS 不起作用
是的,不起作用。接下来,让我们按照(是的,再一次,我回归到比我懂得多的人)Karlo van Wyk 在这里提供的说明 创建一个自签名证书。基本上,请原谅我实质上抄袭了他的说明,创建一个文件夹,并在该文件夹中创建一个名为 localhost.conf 的文件。您可以使用 nano 编辑器,出于某种原因,说明说要通过 sudo nano
以超级用户身份启动 nano
,我不知道为什么。接下来,复制他提供的配置文件(我真的不想再在这里发布它,但为了本文的完整性,似乎有必要)
[req]
default_bits = 2048
default_keyfile = localhost.key
distinguished_name = req_distinguished_name
req_extensions = req_ext
x509_extensions = v3_ca
[req_distinguished_name]
countryName = Country Name (2 letter code)
countryName_default = US
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = New York
localityName = Locality Name (eg, city)
localityName_default = Rochester
organizationName = Organization Name (eg, company)
organizationName_default = localhost
organizationalUnitName = organizationalunit
organizationalUnitName_default = Development
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = localhost
commonName_max = 64
[req_ext]
subjectAltName = @alt_names
[v3_ca]
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = 127.0.0.1
随意编辑您的本地信息。
接下来,创建证书密钥对(公钥和私钥)
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048
-keyout localhost.key -out localhost.crt -config localhost.conf
然后将它们以超级用户身份复制到 /etc/ssl 文件夹(这样您就拥有写入权限)
sudo cp localhost.crt /etc/ssl/certs/localhost.crt
sudo cp localhost.key /etc/ssl/private/localhost.key
最后,使用 sudo nano
编辑 default 文件,就像我们上面做的那样
sudo nano /etc/nginx/sites-available/default
取消注释 SSL 配置行并添加几行额外的,使其看起来像这样
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_certificate /etc/ssl/certs/localhost.crt;
ssl_certificate_key /etc/ssl/private/localhost.key;
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
最后,像我们之前那样重新加载 nginx 配置
sudo /etc/init.d/nginx reload
然后使用 HTTPS 浏览到树莓派的 IP,您应该会看到这个
显然,连接现在已经建立,Chrome 正在警告您证书无效,这是很明显的,因为我们正在创建测试证书,而不是由 LetsEncrypt 等证书颁发机构颁发的证书。所以,点击“高级”和“继续....”以进入网站。
就这样!
将服务器作为服务运行
理想情况下,网络服务器应该在树莓派启动时自动启动——实现这一点很简单,尽管我确实需要阅读 这篇文章 关于托管 ASP.NET Core 和 这篇文章 关于将一个简单的控制台应用程序设置为服务。这里的“诀窍”是不要执行 Console.ReadLine
,因为这会使启动过程超时,而且显然不能退出应用程序!按照第二篇文章的指导,添加这些命名空间
using System.IO;
using System.Reflection;
并编辑 Program.cs
static readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
static void Main(string[] args)
{
StartService();
}
以及实现(从第二个链接的示例中精简而来)
static void StartService()
{
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
StartServer();
while (!tokenSource.Token.IsCancellationRequested)
{
Thread.Sleep(1000);
}
}
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
tokenSource.Cancel();
}
提示:while 循环可能可以通过一个等待直到令牌被取消的任务来改进。
接下来,在 /lib/systemd/system 文件夹中创建一个 .service 文件,其内容如下(使用 sudo nano
创建和编辑文件)
[Unit]
Description=Web Server
After=nginx.service
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/webserver
ExecStart=/home/pi/webserver/consoleApp
Restart=always
[Install]
WantedBy=multi-user.target
如您所见,我将 consoleApp(及其文件)放在一个名为 webserver 的文件夹中,并且我还将服务文件命名为 webserver.service。
一旦服务文件创建,创建一个软链接以方便引用
sudo systemctl enable webserver
您可以立即手动启动服务器
sudo systemctl start webserver
并使用以下命令检查其状态
systemctl status webserver.service
提示:当您查看服务状态时,任何控制台输出都会被记录和显示!
您应该会看到类似这样的内容
我们还可以使用 htop
查看正在运行的服务:
我发现错误通常能很好地指示问题。
修订
- 2019年1月11日:新增将网络服务器设置为服务
结论
这篇文章完成了不少事情
- 确定树莓派版本和功能
- 将操作系统映像写入 USB 驱动器
- 设置 SSH 以便我们使用 PuTTy 和 WinSCP 与树莓派通信
- 安装 Postgres 并创建测试数据库
- 安装 .NET Core 2.2 并测试 Postgres 连接性,包括从 Windows 电脑和直接在树莓派上
- 创建一个“回显”HTTP 服务器
- 安装和配置 nginx 以用于 HTTP
- 配置 nginx 作为我们 .NET Core “回显”服务器的反向代理
- 使用测试证书配置 nginx
而这里最大的成就是,我们所有这些都无需使用 ASP.NET Core。坦率地说,很难找到与 ASP.NET Core 无关的关于 nginx、设置 HTTPS 等方面的文章,所以希望读者会欣赏我在这里采取的裸机方法。我的下一篇文章将更深入地探讨创建一个真实的网站(从现有网站移植过来),处理性能问题(连接 Postgres 那可怕的 8 秒延迟),以及谁知道还有什么。