65.9K
CodeProject 正在变化。 阅读更多。
Home

为 Linux VPS 构建 ASP.NET Core Web 应用:第 2 部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2016 年 7 月 15 日

CC (ASA 3U)

15分钟阅读

viewsIcon

22566

downloadIcon

130

面向 Windows 程序员的快速指南:为 Linux VPS 和 NGINX 构建安全的 .NET Core Web 应用

引言

本系列第 1 部分中,我们已在 Debian 虚拟专用服务器 (VPS) 上成功运行了 .NET。现在是时候构建一个简单的 ASP.NET Core 1.0 Web 应用,用于向匿名冲浪的水手致以问候了。但首先,我们需要加固我们的 Linux 服务器,它目前仍然以默认设置运行在野外。然后,我们就可以设置 NGINX 并用 C# 构建那个炫酷的应用了。

防火墙

还记得我当初对 VPS 的兴奋之情吗?因为它们能让我以极低的成本在功能齐全的系统上进行开发。如果您选择标准的、简化的 IIS 托管计划,您将无法获得这些,但作为交换,会有许多专家负责整个系统的安全,而且他们在这方面做得比您或我做得好得多。使用 VPS 的自由代价是,您需要自己负责确保最低级别的安全。

在 Linux 中设置防火墙似乎有两种方法。第一种是针对那些真正、真正知道自己在做什么的人,以及/或愿意花几天时间学习的人。如果您在 Google 上搜索 iptables,就可以了解那种方法。我的方法是“简单防火墙”,简称 ufw。以下是如何从命令提示符进行设置。

gabor@debian-512mb-fra1-01:~$ sudo apt-get install ufw
#
# Many lines of output omitted
#
gabor@debian-512mb-fra1-01:~$ sudo ufw status
Status: inactive
gabor@debian-512mb-fra1-01:~$ sudo ufw default deny incoming
Default incoming policy changed to 'deny'
(be sure to update your rules accordingly)
gabor@debian-512mb-fra1-01:~$ sudo ufw default allow outgoing
Default outgoing policy changed to 'allow'
(be sure to update your rules accordingly)
gabor@debian-512mb-fra1-01:~$ sudo ufw allow ssh
Rules updated
Rules updated (v6)
gabor@debian-512mb-fra1-01:~$ sudo ufw allow http
Rules updated
Rules updated (v6)
gabor@debian-512mb-fra1-01:~$ sudo ufw allow https
Rules updated
Rules updated (v6)
gabor@debian-512mb-fra1-01:~$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

(如果您没有阅读第一部分:为了提高可读性,我将用粗体突出显示您自己的命令。)

让我们来分析一下

  • apt-get 安装 ufw
  • 使用 ufw status 检查防火墙是否处于非活动状态。(安装后它应该是这样的。)这一点非常重要。如果您开始禁用所有连接,您可能会丢失您正在使用的 SSH 会话,然后您就会有一个完全自主的服务器,但任何人无法再远程访问它。
  • 接下来的两个命令不言自明:它们禁用所有入站连接,并允许出站连接。有一派会更加警惕出站连接,但那样的话,您就需要额外的工作来下载更新或模块,更不用说从您自己的应用程序调用服务了。
  • 在确定我们的基线是“无入站连接”后,接下来的命令将重新启用特定功能,例如 SSH、HTTP 和 HTTPS 的端口。最关键的是 SSH,否则您将无法重新登录。
  • 最后,ufw enable 使用我们刚刚设置的规则来开启防火墙。

将域名指向您的服务器

如果您已经拥有域名,则无需浪费您的时间。如果您没有,那么您可以做我所做的:在大约五分钟内获取一个免费域名。有几种顶级域名 (TLD) 允许您这样做;我选择了 .tk 域名。在这些文章的其余部分,我将使用 vps-net-core.tk。在您自己的域名的管理界面中,将一个空的“A”记录指向您的 VPS 的 IP 地址;或者,您也可以为 www 子域指向第二个记录。在我的例子中,设置如下所示

官方来说,这可能需要一天时间才能在互联网上传播,但实际上我从未等待超过几分钟。您可以通过从 Windows 命令提示符 ping 您的服务器来检查状态

C:\>ping vps-net-core.tk

Pinging vps-net-core.tk [46.101.137.211] with 32 bytes of data:
Reply from 46.101.137.211: bytes=32 time=41ms TTL=52
Reply from 46.101.137.211: bytes=32 time=27ms TTL=52
Reply from 46.101.137.211: bytes=32 time=62ms TTL=52
Reply from 46.101.137.211: bytes=32 time=26ms TTL=52

Ping statistics for 46.101.137.211:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 26ms, Maximum = 62ms, Average = 39ms

专业提示:如果您真的、真的不想为免费域名注销一个一次性电子邮件地址,您可以修改位于 C:\Windows\System32\drivers\etchosts 文件。该文件中的注释不言自明;唯一的技巧是允许 Windows 和您的本地防火墙修改它。提示:将其复制到桌面;在那里编辑;然后复制粘贴回来,并点击“Mother Doze”的恐吓性确认对话框。当然,您需要管理员权限才能这样做。

可选:Midnight Commander

此时我通常会厌倦裸露的命令行。如果您熟悉 Windows 中的 Norton Commander 或 FAR Manager,那么 Midnight Commander 将是您的朋友。到目前为止,您可能已经猜到了下面的确切咒语;按照它来查看您在 PuTTY 会话中看到的内容(下图)。

gabor@debian-512mb-fra1-01:~$ sudo apt-get install mc
gabor@debian-512mb-fra1-01:~$ mc

提供页面:NGINX

开始提供页面了!我们将设置 NGINX,但在开始之前,需要进行一次简短的语言课。我花费了几个月的时间把它读成“enjinx”,并为随之而来的不可避免的厄运而颤抖。然后我发现它实际上是“Engine X”。我真希望现在就能重新调整我的习惯。

现在,NGINX 类似于 Apache,只是它更容易驾驭,而且消耗的资源也少得多。记住,我们要的是一个极小但仍然像 SpaceX 火箭一样快的服务器,对吧?

但我们为什么还需要 Web 服务器呢?ASP.NET Core 的 Web 组件 Kestrel 不是专门用来处理 HTTP 请求的吗?嗯,部分是的。它确实能做到这一点,但它也做不了多少其他事情。通过多个域名服务多个站点(通过多个域名)?不行。通过 HTTPS 进行安全连接?不行。就我而言,这也是正确的做法:让每个组件处理单一职责,并做得很好。不多说了。让我们在我们的服务器上魔术般地启动 NGINX。

gabor@debian-512mb-fra1-01:~$ sudo apt-get install nginx

现在,打开您最喜欢的浏览器,欣赏 NGINX 的默认欢迎页面

以下是 @carrotcreative 为您提供的 NGINX 的超级酷、超级简洁的概述: http://carrot.is/coding/nginx_introduction

阅读愉快。

一个真正简单的 ASP.NET Core Web 应用

下载第一个代码示例 WCA.zip,然后在 Visual Studio 中运行它。它只是一个“空白”ASP.NET Web 应用程序创建出来的东西的稍微简化版本,除了我对空白的定义似乎与微软的不同。此外,我还更改了 Startup.csProgram.cs 中的一些关键行,以便从 wwwroot 子文件夹提供静态 HTML 文件,但之后我会在本系列中进一步讨论实际的 Web 开发部分。

好的,它能在 Windows 上运行。现在只需将示例解压缩到任何本地文件夹,然后将其上传到您自己的 Debian 主目录(我在此处使用 work/WCA)。您可以通过 WinSCP 完成此操作。如果您不知道那是什么,并且跳过了第一部分,现在是时候阅读一下了。

进入您的主目录中的命令提示符,然后像这样启动它

gabor@debian-512mb-fra1-01:~$ cd work/WCA/src/WCA/
gabor@debian-512mb-fra1-01:~/work/WCA/src/WCA$ dotnet restore
log  : Restoring packages for /home/gabor/work/WCA/src/WCA/project.json...
log  : Writing lock file to disk. Path: /home/gabor/work/WCA/src/WCA/project.lock.json
log  : /home/gabor/work/WCA/src/WCA/project.json
log  : Restore completed in 2501ms.
gabor@debian-512mb-fra1-01:~/work/WCA/src/WCA$ dotnet run
Project WCA (.NETCoreApp,Version=v1.0) will be compiled because expected outputs are missing
Compiling WCA for .NETCoreApp,Version=v1.0

Compilation succeeded.
    0 Warning(s)
    0 Error(s)

Time elapsed 00:00:02.1747842

Hosting environment: Production
Content root path: /home/gabor/work/WCA/src/WCA
Now listening on: http://0.0.0.0:5000
Application started. Press Ctrl+C to shut down.

哈!您现在有一个 Web 应用正在监听端口 5000。但是……您如何才能看到它提供的页面呢?这有点困难,因为您刚才告诉 ufw 不要允许您通过该端口访问。如果您有心情玩一下,您可以做以下两件事之一

  1. 调整 ufw 以允许您访问。
  2. 通过另一个会话安装一个文本浏览器(!)如 Lynx,然后检查 localhost:5000。

或者,您可以直接进入下一节。

反向代理

其思想是让 .NET Core 的“Web 服务器” Kestrel 运行在 localhost 上,仅通过端口 5000 在本地响应调用。但我们也配置 NGINX 作为反向代理,这实际上是一种花哨的说法,意思是它不会直接提供入站请求,而是会将它们直接转发到 localhost:5000,由您的应用程序来响应。

NGINX 的配置文件位于 /etc/nginx,特别是两个子文件夹:/nginx/etc/sites-available/nginx/etc/sites-enabled。基本思想是,您在 sites-enabled 中为您的服务器服务的每个域名保留一个文件。额外的诀窍是,您实际上将这些文件保存在 sites-available 中,并仅从 sites-enabled 指向它们的符号链接。这样,您就可以为一个域名处理多个不同的配置文件:例如,一个定义网站正常运行时的文件,另一个提供“维护中”页面的文件。您可以通过将符号链接指向其中一个来切换。

在我的例子中,我创建了 /etc/nginx/sites-available/vps-net-core.tk,内容如下。(提示:我喜欢用 sudo mc 以 root 权限启动 Midnight Commander,然后使用其内置编辑器来修改像这样的简单文件。)

server {
  listen 80;
  server_name vps-net-core.tk;

  location / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://127.0.0.1:5000;
  }
}

现在创建一个指向该文件的符号链接,删除 NGINX 的默认配置文件,测试是否一切正常,然后重新启动 NGINX

gabor@debian-512mb-fra1-01:~$ ln -s /etc/nginx/sites-available/vps-net-core.tk /etc/nginx/sites-enabled/vps-net-core.tk
gabor@debian-512mb-fra1-01:~$ rm /etc/nginx/sites-available/default
gabor@debian-512mb-fra1-01:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
gabor@debian-512mb-fra1-01:~$ sudo service nginx restart

第三条命令 nginx -t 是一个很棒的小技巧。它会解析当前配置,并在出现问题时给您一个提示。NGINX 在运行时会忽略配置文件中的任何内容,只有在您重新启动时才会读取它们。如果弄乱了它们,直到您设法修复它们之前,NGINX 将一直处于关闭状态。

我们基本上……成功了!如果您仍在另一个终端会话中运行 .NET 应用,您可以导航到您的网站(相当于 vps-net-core.tk)并看到“Hello, sailor.”。如果您的应用未运行,您将看到

以下链接提供了关于将 Kestrel 与 NGINX 连接的更多精彩阅读内容
https://mjomaa.com/computer-science/frameworks/asp-net-mvc/141-how-to-combine-nginx-kestrel-for-production-part-i-installation
http://druss.co/2015/06/asp-net-5-kestrel-nginx-web-server-on-linux/

发布并部署为服务

我们刚刚拼凑了一个系统,只要您从命令行运行 .NET Core 应用(即时编译)就可以提供页面,但这与生产系统相去甚远。首先,您不想上传源代码,而是想在本地编译并部署二进制文件;其次,您希望在 Linux 服务器上运行类似服务的东西,而不是命令行程序。

从 Visual Studio 发布

在您的 Visual Studio 项目中,转到生成 / 发布,然后按照向导创建一个新的“配置文件”。这本质上是一组设置,告诉 VS 如何编译您的代码以及如何处理输出。我们就采用最简单的机制:将输出放在您 PC 的本地文件系统中,然后手动复制(使用 WinSCP)进行部署。在向导的第一步,为发布目标选择“自定义”

在第二步中,在为配置文件命名后,保持选择“文件系统”并选择一个合适的文件夹。我喜欢将此文件夹放在项目文件夹层次结构之外;毕竟,它不是我代码的一部分,只是一个可以从中部署的跳板。我讨厌那些脑残的 \bin\Release\PublishOutput 默认设置。

其余的都很直观,每次发布 Web 应用的一个版本时,您都可以再次使用此“配置文件”。我上传了一个编译输出的示例 WCA-published.zip。查看里面的内容;它包含您的项目输出以及运行它所需的所有依赖项。

我的部署方式是:

  1. 使用 WinSCP 将原始(未解压缩)文件上传到您的 Linux 用户主目录下的一个子目录中
  2. 在 Linux shell 中,将此目录的内容复制到 /opt/WCA-app/app。您首先需要创建 /opt 中的目录,并且您需要 root 权限(sudo)才能执行这两项操作和复制操作。

现在切换到目标目录并像这样运行您的 dotnet 应用

gabor@debian-512mb-fra1-01:/opt/WCA-app/app$ dotnet WCA.dll

请注意,我们没有像以前那样使用 dotnet run:那会编译并执行项目目录中的代码。这个不同:我们正在执行一个编译好的二进制文件。说实话,这太酷了。非常非常酷。我们只是将一堆二进制 DLL 文件从 Windows 服务器复制到 Linux 服务器上并“原生”执行它们!捏捏我。

设置为服务

在设置生产环境时,有三个方面需要考虑

  1. 什么是正确的目录结构?
  2. 哪个用户将运行该服务,拥有什么权限?
  3. 如何打包您的应用程序以使其充当服务,在系统启动时启动并响应标准命令?

目录结构。 我希望我能找到任何最佳实践,但似乎运行 .NET Core Web 应用在 Linux 上的用户还不是很多。所以只能凭常识。请带着批判性的眼光阅读所有这些内容,如果您有任何异议或更好的建议,请在评论中告诉我。

  • 我喜欢为站点的编译输出保留一个专用目录,因此有 /app 子文件夹。关键是,应用程序不应该能够在此处写入任何内容,而且我应该能够盲目地将完整的发布包复制到此空间。
  • 那么,整个应用程序(包括数据、配置文件等)最初应该放在哪里?我选择放在 /opt 下的一个子目录,假设这符合 Linux 放置自定义文件的习惯。毕竟,dotnet 也安装在那里,Let's Encrypt 也是如此(下次再回来阅读有关它的内容)。
  • 除了可执行文件和静态内容外,我的 Web 应用通常还需要另外两种类型的空间。一种用于系统特定的配置文件,例如数据库连接字符串。您希望在开发机器上有一个环境,在 VPS 上有一个生产环境。您不希望交叉污染这两个环境的配置文件,因此在部署编译输出时,它们需要放在一边。这就是为什么我有 /app 子文件夹,并有足够的空间容纳一个邻近的 /config 文件夹。
  • 最后,您的应用程序可能需要自己的安全地带,用于写入和删除文件:例如,上传的临时文件、日志等。这些都可以放在一个或多个额外的、邻近的子目录中,您授予执行用户写入权限。但这些文件夹绝不应该通过您的网站的公共视图直接通过 URL 访问。

执行用户。 我们稍后将看到如何在 Linux 上将您的应用程序执行为“服务”。似乎除非您付出额外的努力,否则这样的服务默认将以 root 身份运行,我对此一点也不放心。以下是我发现的一种适用于 Linux 标准权限方案的解决方案。可能有一个更复杂的 ACL 方法,但我看到所需的命令数量巨大,而且即使那些命令也会因您使用的文件系统而异,这让我失去了兴趣。

gabor@debian-512mb-fra1-01:~$ sudo groupadd wca-users
gabor@debian-512mb-fra1-01:~$ sudo adduser --system --no-create-home wca-app
Adding system user `wca-app' (UID 108) ...
Adding new user `wca-app' (UID 108) with group `nogroup' ...
Not creating home directory `/home/wca-app'.
gabor@debian-512mb-fra1-01:~$ sudo usermod -a -G wca-users wca-app
gabor@debian-512mb-fra1-01:~$ sudo chown -R wca-app:wca-users /opt/WCA-app
gabor@debian-512mb-fra1-01:~$ sudo chmod -R 550 /opt/WCA-app
gabor@debian-512mb-fra1-01:~$ sudo chmod -R g+s /opt/WCA-app
gabor@debian-512mb-fra1-01:~$ sudo -u wca-app dotnet /opt/WCA-app/app/WCA.dll

英文

  • groupadd wca-users 创建一个名为“wca-users”的组。
  • adduser --system --no-create-home wca-app 创建一个系统用户(无法交互登录),该用户没有主目录,名为“wca-app”。
  • usermod -a -G wca-users wca-app 将新用户添加到“wca-users”组。
  • chown -R wca-app:wca-users /opt/WCA-app 将我们的应用程序文件夹的所有权更改为新用户和新组。
  • chmod -R 550 /opt/WCA-app 递归地为我们应用程序目录的拥有用户和组授予读和执行权限,但不对其他任何人授予任何权限。
  • chmod -R g+s /opt/WCA-app  是关键。引用维基百科对它的解释:它“使在此目录中创建的新文件和子目录继承其组 ID,而不是创建文件(所有者 ID 永不更改,仅组 ID)的用户的主要组 ID。”换句话说,如果其他用户在我的应用程序目录中创建文件,这些文件将继承父目录的组 ID。用 Windows 的话来说,就是继承的权限。显然,Linux 中的用户无法做到这一点,只有组可以:这就是我们最初需要组的原因。我需要这一切的原因当然是我将来希望以 root 身份复制部署的文件,但我想保留我的权限架构。
  • sudo -u wca-app dotnet /opt/WCA-app/app/WCA.dll 最后,以 wca-app 用户的名义执行 Web 应用,以验证一切是否正常。

Shell 脚本。 作为最后一件组件,我们需要一个 shell 脚本,它能够以 wca-app 的名义将 Web 应用作为后台进程启动、停止并根据需要重新启动。然后,我们可以将脚本战略性地放置在 /etc/init.d 这个神奇的文件夹中,并告诉 Linux 在系统启动时启动我们的服务。为此,我大量参考了 druss 的脚本,但由于两个原因进行了修改。第一,他的脚本是针对 .NET Core 的预发布版本的,从那时起,所需的命令已更改为 dotnet。第二,他的脚本在我的权限方案下无法正常工作。这是我最终得到的脚本

#!/bin/sh
### BEGIN INIT INFO
# Provides:          wca-srv
# Required-Start:    $local_fs $network $named $time $syslog
# Required-Stop:     $local_fs $network $named $time $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Description:       Script to run asp.net 5 application in the background
### END INIT INFO
 
# Author: Ivan Derevianko aka druss <drussilla7@gmail.com>
# Modified by: Gabor L Ugray
 
SRVUSER=wca-app
MODULEROOT=/opt/WCA-app
APPROOT=$MODULEROOT/app
APPDLL=WCA.dll
PIDFILE=$MODULEROOT/service/service.pid
LOGFILE=$MODULEROOT/service/service.log
 
# fix issue with DNX exception in case of two env vars with the same name but different case
TMP_SAVE_runlevel_VAR=$runlevel
unset runlevel
 
start() {
  if [ -f $PIDFILE ] && kill -0 $(cat $PIDFILE); then
    echo 'Service already running' >&2
    return 1
  fi
  echo 'Starting service...' >&2
  #su -c "start-stop-daemon -SbmCv -x /usr/bin/nohup -p \"$PIDFILE\" -d \"$APPROOT\" -- \"$DNXRUNTIME\" kestrel > \"$LOGFILE\"" $WWW_USER
  su -c "start-stop-daemon -SbmCv -x /usr/bin/nohup -p \"$PIDFILE\" -c \"$SRVUSER\" -d \"$APPROOT\" -- /usr/bin/nohup dotnet \"$APPROOT/$APPDLL\" > \"$LOGFILE\" 2>&1"
  echo 'Service started' >&2
}
 
stop() {
  if [ ! -f "$PIDFILE" ] || ! kill -0 $(cat "$PIDFILE"); then
    echo 'Service not running' >&2
    return 1
  fi
  echo 'Stopping service...' >&2
  start-stop-daemon -K -p "$PIDFILE"
  rm -f "$PIDFILE"
  echo 'Service stopped' >&2
}
 
case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  restart)
    stop
    start
    ;;
  *)
    echo "Usage: $0 {start|stop|restart}"
esac
 
export runlevel=$TMP_SAVE_runlevel_VAR


关键行是负责启动进程以运行 dotnet 并运行您的应用程序的那一行。下面是 druss 的原始版本以及我的修改版本。

#su -c "start-stop-daemon -SbmCv -x /usr/bin/nohup -p \"$PIDFILE\" -d \"$APPROOT\" -- \"$DNXRUNTIME\" kestrel > \"$LOGFILE\"" $WWW_USER
su -c "start-stop-daemon -SbmCv -x /usr/bin/nohup -p \"$PIDFILE\" -c \"$SRVUSER\" -d \"$APPROOT\" -- /usr/bin/nohup dotnet \"$APPROOT/$APPDLL\" > \"$LOGFILE\" 2>&1"

主要区别在于,他的版本已经以服务用户的名义调用了 start-stop-daemon,而我的版本仍然以 root 的名义调用 start-stop-daemon,并告诉它以服务用户的名义执行 dotnet 作为后台进程。

这个脚本(或更具体地说,start-stop-daemon)的另一个作用是,当服务启动时创建一个“pidfile”或进程 ID 文件,其中包含正在运行进程的 ID。它用作指示器,用于知道服务是否正在运行,以及在停止服务时终止正确的进程。我选择将此文件保存在应用程序部署目录之外,而是将其放在 /opt/WCA-app/service 中。

部署脚本

  • 上传到您的主文件夹。我称之为 wca-srv.sh
  • 测试它:./wca-srv.sh start
  • 复制到 init.d(您需要 sudo;省略 .sh)

那么

gabor@debian-512mb-fra1-01:~$ sudo chmod 755 /etc/init.d/wca-srv
gabor@debian-512mb-fra1-01:~$ sudo /etc/init.d/wca-srv start
Starting service...
Service started
gabor@debian-512mb-fra1-01:~$ sudo update-rc.d wca-srv defaults
gabor@debian-512mb-fra1-01:~$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.9  28580  4676 ?        Ss   Jul09   0:19 /sbin/init
root         2  0.0  0.0      0     0 ?        S    Jul09   0:00 [kthreadd]
#
# Many lines omitted
#
wca-app    601  0.0 12.0 7077296 60776 ?       SLl  Jul09   0:37 dotnet /opt/WCA-app/app/WCA.dll
#
# More lines omitted
#

解压

  • chmod 755 /etc/init.d/wca-srv 使其可执行
  • /etc/init.d/wca-srv start 启动服务
  • update-rc.d wca-srv defaults 设置服务在系统启动时启动。(以后禁用它,可以使用 update-rc.d -f wca-srv remove。)
  • ps aux 列出您系统上运行的所有进程以及与之关联的用户。我用它来验证它是否真的如预期的那样以 wca-app 身份运行。

在浏览器中加载您的站点以验证 .NET Core 应用是否正在运行。重启系统以验证它是否在启动时启动。

有关将 .NET Core 应用设置为服务以及配置权限的一些有用链接

http://druss.co/2015/06/run-kestrel-in-the-background/
http://xmodulo.com/how-to-automatically-start-program-on-boot-in-debian.html
http://www.yolinux.com/TUTORIALS/LinuxTutorialManagingGroups.html

结论

这有点像一场马拉松!但现在您已经成功部署了一个简单的 .NET Core 应用作为 Linux 上的服务,并且您已经掌握了一个构建和部署流程的基础知识,这将使您能够专注于实际开发。除了……我们仍然没有 HTTPS。请查看第 3 部分,了解如何设置 Let's Encrypt 的免费证书。

历史

2016 年 7 月 15 日 - 初始版本

为 Linux VPS 构建 ASP.NET Core Web 应用:第 2 部分 - CodeProject - 代码之家
© . All rights reserved.