最近沉迷《宝可梦 剑/盾》,导致博客更新晚了 😂 甚至想写一篇关于《宝可梦》的文章
背景
这几年我一直在使用 Vultr 的服务器,在日本东京有两台服务器:
- 一台是 2.5 美元一个月的 Cloud Compute,用作博客的 Web 服务器,拥有 1 虚拟CPU核心、512MB 内存、20GB SSD
- 另一台是 6 美元一个月的 High Frequency,用作博客的 Db 服务器,拥有 1 虚拟CPU核心、1024MB 内存、32GB NVMe SSD
但是今年(2019年)9月底,两台服务器都变得异常难以访问:网页加载奇慢无比,SSH 连接会经常断开,ping 基本处于丢包状态,但从海外访问则没有这种问题,应该是 Vultr 的 IP 段被和谐了,从此萌生了改用香港主机或干脆用国内服务器的想法。
然鹅,我的博客使用的 .dev
域名在国内无法进行备案,于是只能选在离我博客的受众群体尽可能的近的地方——香港安家。因为一直没用过微软 Azure,就在 Azure 开了 1 美元试用,具体细节可能日后发文章再说,反正和 Vultr 比起来挺贵的……Azure 只有用虚拟机相对便宜,要使用 App Service 还得买单独的 Azure Database for PostgreSQL,太贵了。我现在使用的是一台 B1ms 的机器,配置为 1 虚拟CPU核心、2048MB 内存、30GB HDD,这台机器一个月至少需要21刀,是 Vultr 的 2.5 倍多…不过嘛,信仰第一~
随着前两周博客正式上线,我也开始着手将博客环境从 Vultr 迁移到 Azure,这就免不了重新配置服务器环境,本文就是这次重新配置的记录。
如何在 Azure 控制面板中创建虚拟机就不多赘述了,不过需要先简单介绍一下:
- 我使用的系统是 Debian
- 呼呼小笼包的博客 由基于 ASP.NET Core 开发的 Web API BunBlog.API以及一个需要在服务器端渲染的站点 BunBlog.UI 组成
- BunBlog.API 依赖 PostgreSQL 数据库
配置 SSH 连接
对于熟悉 Linux 的童鞋们来说,SSH 一定不陌生。
简单说,SSH是一种网络协议,用于计算机之间的加密登录。 —— 阮一峰 《SSH原理与运用(一):远程登录》
要把 SSH 配置好了,我们才能(比较舒服的)远程操作我们的服务器。
生成 SSH 公钥和私钥
为了提高安全性,我们使用 SSH 公钥 的方式来进行身份验证,而不是传统的用户名密码方式。因为使用的是 Windows 系统,我习惯使用 PuTTY 附带的 puttygen.exe
来生成密钥。
启动 puttygen.exe
后,我们在窗口最底部的 Parameters
中选择,生成一个 SSH-2 RSA
类型,2048
位的密钥。然后点击 Actions
中的 Generate
按钮,之后四处晃动鼠标(鼠标的位置相当于随机值)直到进度条满。
然后在新显示出来的 Key passphrase
(密码) 以及 Confirm passphrase
(确认密码) 文本框中输入密码,尽可能复杂,只有知道这个密码才能使用我们的密钥。最后点击 Actions
中的 Save public key
(保存公钥) 以及 Save private key
(保存私钥,后缀名为 .ppk
) 将公钥和私钥妥善保存。
最后将生成的公钥内容复制并粘贴到创建虚拟机第一步的“SSH 公钥”文本域中,并确保 SSH 使用的 22
端口允许入站。
需要注意的是,如果想让 Xshell 之类的客户端也能使用,则需要用 puttygen.exe
菜单中的 Conversions
- Export OpenSSH key
来导出一个 OpenSSH key。如果你已经关闭了 PuTTY 程序,可以用 Load
按钮来载入之前保存的私钥文件。
通过 PuTTY 连接
PuTTY 是一款非常小巧的 SSH 客户端,可以用它通过 SSH 连接服务器,首先启动 PuTTY。
将服务器的 IP 或域名填入 Host Name
。
然后将左侧树状菜单切换到 Connection
- Data
,并在右侧的 Auto-login username
文本框中输入用户名。
接着切换到 SSH
- Auth
,点击 Browse...
按钮选择 puttygen.exe
生成的私钥文件。
最后从左侧树状菜单回到最开始的 Session
下,在 Saved Sessions
文本框中输入本配置的名字,然后点击 Save
按钮,即可保存配置,方便下次使用,如果不想保存,直接点击 Open
按钮即可开始连接。连接时会要求输入我们设置的密码,输入密码后就能连接到服务器上了。
通过 Xshell 连接
因为我是个人使用,我觉得 Xshell 是一款十分方便的 SSH 客户端,不过需要注意的是,商业环境使用是收费的。这里也介绍一下 Xshell 的 SSH 连接设置。
首先启动 Xshell,按下组合键 Alt + N 打开“新建会话属性”窗口,分别设置名称
(本配置的名称)和主机
(服务器的 IP 或者域名)。
然后在左边树状菜单中选中“用户身份验证”,将方法
下拉菜单修改为Public Key
,在用户名
中输入用户名。接下来点击用户密钥
右侧的 浏览...
按钮,在弹出的“用户密钥”窗口中点击导入
按钮,找到之前生成的 OpenSSH key(不是公钥,也不是后缀为 .ppk
的私钥,是通过 Export OpenSSH key
导出的那个文件),输入在 puttygen.exe
中设置的密码即可导入,选中导入的密钥然后点击确定
按钮,保存会话的配置。
最后按下组合键 Alt + O 打开“会话”窗口,双击刚才新建的会话即可开始连接。和通过 PuTTY 连接一样,需要输入密钥的密码。
安装 .NET Core 运行环境
因为 BunBlog.API
含有一个 ASP.NET Core 开发的 Web API,所以我们需要安装 .NET Core 的运行时。访问 https://dot.net 找到对应的包,需要下载一个 .NET Core 3.0 的 Linux 的运行时,在这个地址,切换到 Debian 10,然后依此执行页面上给出的代码即可。
注意,执行官网的代码可能会遇到这样的错误提示 gpg: command not found
,是因为系统没有安装 GnuPG,安装好即可
sudo apt-get install gnupg
下面复制了微软官方的安装命令,仅供参考,请以官网最新的内容为准
# Register Microsoft key and feed
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.asc.gpg
sudo mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/
wget -q https://packages.microsoft.com/config/debian/10/prod.list
sudo mv prod.list /etc/apt/sources.list.d/microsoft-prod.list
sudo chown root:root /etc/apt/trusted.gpg.d/microsoft.asc.gpg
sudo chown root:root /etc/apt/sources.list.d/microsoft-prod.list
# Install the .NET Runtime
sudo apt-get update
sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install aspnetcore-runtime-3.0
安装完成后,我们通过 dotnet --info
命令来查看是否安装成功:
It was not possible to find any installed .NET Core SDKs
Did you mean to run .NET Core SDK commands? Install a .NET Core SDK from:
https://aka.ms/dotnet-download
Host (useful for support):
Version: 3.0.0
Commit: 95a0a61858
.NET Core SDKs installed:
No SDKs were found.
.NET Core runtimes installed:
Microsoft.AspNetCore.App 3.0.0 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 3.0.0 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
To install additional .NET Core runtimes or SDKs:
https://aka.ms/dotnet-download
如果 .NET Core runtimes installed 列表下和我们安装的 .NET Core runtime 一致,那么恭喜,.NET Core 3.0 和 ASP.NET Core 的环境就安装好了~
安装 Node.js
通过官网 https://nodejs.org 找到通过包管理器安装 Node.js 的方法:Installing Node.js via package manager,选 Debian ,又被带到一个 GitHub 页面,再点击一下“Debian and Ubuntu based distributions” 下方的 “Installation instructions” 就能跳转到 Debian 的安装命令。
注意,后面的步骤需要用到 curl
,如果没有记得提前安装好
sudo apt-get install curl
这里我安装最新版本,下面复制了Node.js官方的安装命令,仅供参考,请以官方最新的内容为准
# Node.js v13.x
# Using Debian, as root
curl -sL https://deb.nodesource.com/setup_13.x | bash -
apt-get install -y nodejs
安装完成后,执行下面的命令检查 Node.js 和 Npm 是否正常安装
node --version
npm --version
如果两个命令都返回了版本号,基本就可以认为安装成功了
安装 PostgreSQL
还是一样的套路,在官网 https://www.postgresql.org/ 找到 Linux 的安装方式 PostgreSQL: Linux downloads (Debian)。页面上有一个下拉菜单“Choose your Debian version”(选择你的 Debian 版本),我这里选择 “Buster (10.x)” ,然后按照官网的步骤安装即可。
需要注意的是,第二步:
Create the file /etc/apt/sources.list.d/pgdg.list, and add a line for the repository:
deb http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main
是要求创建 /etc/apt/sources.list.d/pgdg.list
文件,并增加一行内容,而不是执行 deb http://...
汇总一下,修改了文件后需要执行这些命令,仅供参考,请以官方最新的内容为准
# Add repository
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
# install core database server
apt-get install postgresql-11
安装好 PostgreSQL 之后,使用 psql
命令启动客户端,另外它会自动创建一个名为 postgres
的用户,我们通过 su
命令切换到 postgres
用户,然后并执行 psql
命令
sudo su postgres -c psql
如果正常进入到 psql 控制台,则说明安装成功,输入 \q
然后按下 Enter 退出 psql 控制台
创建数据库用户
安装后自动创建的 postgres
用户相当于超级管理员,为了更好的安全性,我们应该使用一个权限较低的用户提供给 BunBlog.API 程序使用。首先我们创建一个名为 bunblog
的系统用户:
sudo adduser bunblog
然后使用 postgres
用户在 psql 中创建数据库用户、数据库:
sudo su postgres -c psql
(下面的命令是在 psql 控制台中执行的)
postgres=# CREATE USER bunblog WITH PASSWORD 'YOUR_PASSWORD';postgres=# CREATE DATABASE bunblogdb;postgres=# GRANT ALL PRIVILEGES ON DATABASE bunblogdb to bunblog;```
上面的
- 第一行创建了一个名为 `bunblog` 密码为 `YOUR_PASSWORD` 的数据库用户
- 第二行创建了名为 `bunblogdb` 的数据库
- 第三行将数据库 `bunblogdb` 的所有权限赋予 `bunblog` 用户。
执行完成后输入 `\q` 退出 psql 控制台,然后用我们刚刚创建的用户来登录。这里需要使用 `-d` 指令设置我们要访问的数据库名称:
```bash
sudo su bunblog -c 'psql -d bunblogdb'
执行后如果成功进入 psql 控制台,则配置完成~
安装 Nginx
nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server, originally written by Igor Sysoev. —— nginx
nginx (读音:engine x)是 HTTP 和反向代理服务器、邮件代理服务器和通用 TCP/UDP 代理服务器,最初由 Igor Sysoev 编写。—— 译自 nginx
由于 BunBlog.API 和 BunBlog.UI 都只暴露两个内网端口,不直接接入公网,需要一个 Web 服务器接入到公网,并且能够反向代理这两个内网的程序,nginx 是常见的选择。
Nginx 直接通过包管理器即可安装
sudo apt update
sudo apt install nginx
安装完成后可以通过公网 IP 或者事先绑定好的域名访问服务器,能看到 nginx 的欢迎页面说明安装成功了。
部署 BunBlog.API
经过一系列的配置,终于开始实际部署程序了!
获取最新版本的程序
从 GitHub 上获取最新的 release 包的地址,并下载到服务器上
wget https://github.com/huhubun/BunBlog.API/releases/download/v0.1.0/BunBlog.API.zip
下载好后使用 unzip
命令解压缩,如果没有这个命令,可以通过 sudo apt-get install unzip
安装。我一般将站点放在 /var/www
下面,所以添加参数 -d
指定解压路径
sudo apt-get update
sudo apt-get install unzip -y
sudo unzip BunBlog.API.zip -d /var/www
这样在 /var/www
目录下,就有了 BunBlog.API
文件夹,这里面的就是博客的 Web API 程序。
创建表结构
另外还有一个数据库脚本,用于创建博客所需的表结构,如法炮制,我们将它从 GitHub 的 release 中下载下来
sudo wget https://github.com/huhubun/BunBlog.API/releases/download/v0.1.0/script.sql -P /var/www/BunBlog.API/
然后在 psql 控制台中执行这个脚本,创建相关表结构
sudo su bunblog -c 'psql -d bunblogdb'
(下面的命令均在 psql 控制台中执行,注意 bunblogdb=#
不需要键入到控制台中)
bunblogdb=# \i /var/www/BunBlog.API/script.sql
\i
表示要执行指定路径的脚本,执行完毕后我们可以通过 \d
查看结果,成功的话则会新增很多张表。
然后为 BunBlog.API 的配置文件 appsettings.Production.json
添加数据库连接字符串即可
"ConnectionStrings": {
"BunBlogConnection": "Host=localhost;Database=bunblogdb;Username=bunblog;Password=YOUR_PASSWORD"
}
监视 BunBlog.API
参见官方文档:监视应用
其它细节
Owner
因为我们的命令全是 sudo
执行的,解压后的程序所有者都是 root
,这可能会导致日志文件不能正常生成的问题,按照上一步按官方文档,我们使用 www-data
用户来运行 BunBlog.API,所以我们将所有程序都设为 www-data
所有
sudo chown -R www-data:www-data /var/www/BunBlog.API/
目录格式的差异
在 Windows 系统中我们用 \
反斜线来分割目录,例如 E:\temp
。而在 Linux 中,使用的是 /
斜线分割:/var/www
这就导致,虽然 .NET Core 程序可以跨平台运行,但我们的一些配置并不是 Windows 和 Linux 都通用的。例如 nlog.config
文件在 Linux 下需要将 internalLogFile
设置成为 /tmp/internal-nlog.txt
。
部署 BunBlog.UI
参见上一篇博文:使用 Nuxt.js 搭建博客前端
配置 Nginx
我们可以直接修改 Nginx 的配置文件 /etc/nginx/nginx.conf
,其中的每一个 server
就相当于一个站点。
BunBlog.API
参见微软官方文档:配置 Nginx
BunBlog.UI
和 BunBlog.API
的配置一样,只需要将 proxy_pass
指向 pm2 启动后,对应的内网地址即可。
HTTPS 证书
因为我是 .dev
域名,强制开启了 HTTPS 访问,因此需要准备 HTTPS 证书并配置到 Nginx 中。这里我使用的是 Let’s Encrypt 的证书,并通过 certbot
来自动生成证书。
首先安装 certbot
sudo apt-get update
sudo apt-get install certbot
由于我的域名解析在 Cloudflare 上,certbot
的插件 certbot-dns-cloudflare
可以在验证域名时自动化的完成一些操作,下面的步骤可能略有不同,但这个插件并不是必须的。可以通过 https://certbot.eff.org/docs/using.html#dns-plugins 查看你的域名解析供应商是否有插件,如果没有可以参考官方文档一步一步配置 https://certbot.eff.org/lets-encrypt/debianbuster-other ,或者将你的域名也迁移到 Cloudflare 进行解析。
安装 certbot-dns-cloudflare
插件
sudo apt-get install python3-certbot-dns-cloudflare
配置 certbot-dns-cloudflare
插件,参阅 https://certbot-dns-cloudflare.readthedocs.io/en/stable/
生成证书
sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/certbot/cloudflare.ini -d 域名
证书生成后存放在 /etc/letsencrypt/live/bun.dev/域名/
目录下,将该地址配置到 Nginx 中作为证书地址即可,下面以 BunBlog.API 为例,演示了一个最终配置好的 Nginx server 节点:
server {
listen 443 ssl http2;server_name api.bun.dev;ssl on;ssl_certificate /etc/letsencrypt/live/bun.dev/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/bun.dev/privkey.pem;location / {
proxy_pass http://localhost:5200;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection keep-alive;proxy_set_header Host $host;proxy_cache_bypass $http_upgrade;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;}
}
修改配置后重启 Nginx
sudo service nginx restart
即可看到效果
安全 Header
为了增强安全性,我们可以让 Nginx 生成这些 header
add_header X-Frame-Options DENY;add_header X-Content-Type-Options nosniff;add_header X-XSS-Protection "1;mode=block";```
这几行的作用分别是
1. 不允许站点在 frame 中展示(在网上搜索“点击劫持”能了解到是什么回事)
2. 禁用客户端 MIME 类型嗅探,可以在 MDN [X-Content-Type-Options](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/X-Content-Type-Options) 找到详细的解释
3. 启用 <abbr title="Cross-site scripting">[跨站脚本攻击(XSS)](https://developer.mozilla.org/zh-CN/docs/Glossary/Cross-site_scripting)</abbr> 保护,并在检查到XSS攻击时,停止渲染页面
至此我们就全部配置完成,最后来 [SSL Labs](https://www.ssllabs.com/) 跑个分吧~
