在 Ubuntu 服务器上配置并启动远程开发服务器
在这篇教程里,我们将在一台全新 Ubuntu 服务器上配置初始环境并启动远程开发服务器。
这个服务器用于开发,所以所有的服务都会使用 npm run dev 等方式在 tmux 终端复用器中启动,而不是正经的启动为服务。
我们将为各服务绑定域名,以便于访问。
我们将配置基于 Cloudflare Zero Trust 的权限系统来保护只有特定的开发者才能访问服务器并进行远程开发。
基本环境配置
我们从全新的 Ubuntu 24.04.1 LTS 系统开始。
在这一步,我们需要安装基本开发工具、Node 和 .NET SDK
首先我们通过 SSH 连接到服务器。
在进行任何操作之前,首先你需要关闭 SSH 的密码登录,上传一个证书公钥,最好再改个 SSH 连接的端口号。
先进行一下apt update。
我们安装下面几个东西:
1. Git
很多时候系统装好了会自动装好git,如果已经装好了可以忽略。
安装也很简单,就只需要 apt install git 就好了。
2. NodeJS
我建议使用 fnm 来安装。
curl -o- https://fnm.vercel.app/install | bash
fnm install 24
# 可以用下面的命令来验证安装情况
node -v
npm -v
# 正常安装的话会打印出版本号fnm 的安装脚本可能会报错,就像下面这样:
root@FWzgJRtz4O2X:~# curl -o- https://fnm.vercel.app/install | bash
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 5891 100 5891 0 0 71775 0 --:--:-- --:--:-- --:--:-- 72728
Checking dependencies for the installation script...
Checking availability of curl... OK!
Checking availability of unzip... Missing!
Not installing fnm due to missing dependencies.仔细看脚本的输出,这种情况是因为 unzip 没有安装,于是使用 apt install unzip 安装即可。
如果看不出来,可以将输出复制给AI,让AI提供安装建议。
3. .NET SDK
注意: .NET SDK 需要安装对应的版本。目前 CCXC Engine 的后端使用 .NET 8 开发,但是即将升级到 .NET 10。
在这篇教程中,我将使用 .NET 8 作为例子,在实际安装中,请按照当前 CCXC Engine 实际使用的 .NET 版本来安装。
注意: 由于我们是开发服务器,所以我们安装的是包含开发工具的 SDK。如果只需要运行,那么建议安装 ASP.NET Core Runtime
由于 Ubuntu 官方已经决定在官方源中维护 .NET SDK 组件包,所以我们不再需要单独添加微软官方的包源。
直接使用下面的命令来安装:
# 对于目前的 CCXC Engine
apt install dotnet-sdk-8.0
# 对于未来的 CCXC Engine
apt install dotnet-sdk-10.0特别说明: 由于 .NET 程序需要一些依赖包,尽管我们使用独立发布(即将.NET运行时和程序打包在一起发布,也称之为 Self-contained 或 自包含应用),在运行时也安装
aspnetcore-runtime-8.0,来防止某些依赖缺失。当然自己手工安装依赖包也是可以的。
4. 数据库
安装 MariaDb Server,然后进行初始化。
apt install mariadb-server
mysql_secure_installation由于我们需要外部连接数据库,所以当询问你
Switch to unix_socket authentication [Y/n]的时候,记得回答 n。
接下来,会询问是否修改 root 用户密码,这里可以回答 n(就是保持空密码不修改),但是我建议是回答 Y 修改一下。最好是随机生成一个密码。
接下来会询问你是否移除匿名用户:
Remove anonymous users? [Y/n]建议回答 Y
接下来会询问你是否不允许 root 用户远程登录:
Disallow root login remotely? [Y/n]由于我们这里是配置远程开发服务器,所以需要回答 n
接下来的问题都直接回车(使用默认值)即可。
完成初始化后,我们需要为数据库创建一个独立用户供设备使用。
首先使用下面的命令登录数据库命令行:
mysql -uroot -p回车后可能会问你 root 密码,填入即可。
运行下面的 SQL 语句来建一个新用户和一个库,并将这个数据库的权限赋给这个用户。
CREATE DATABASE ccxc_dev CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'ccxc_user'@'localhost' IDENTIFIED BY '<这里换成一个随机密码>';
GRANT ALL PRIVILEGES ON ccxc_dev.* TO 'ccxc_user'@'localhost';
FLUSH PRIVILEGES;注意: mariadb 用户将自动获得他权限范围内的
information_schema库的读取权限,所以不需要专门为information_schema库赋权。如果在之后的步骤中,ccxc-backend服务报错显示information_schema库的权限问题,则说明用户的原数据库权限不正确。
由于这是一个远程开发服务器,我们需要再增加一个用户专用于远程访问,这将有助于保持安全。
CREATE USER 'ccxc'@'%' IDENTIFIED BY '<这里换成一个随机密码>';
GRANT ALL PRIVILEGES ON ccxc_dev.* TO 'ccxc'@'%';
FLUSH PRIVILEGES;接下来我们将 mariadb 服务器监听到公网:
编辑:/etc/mysql/mariadb.conf.d/50-server.cnf 文件。找到其中 bind-address 这一行,将等号后面的部分修改为:0.0.0.0。
然后在上面一行插入:
# 随便选择一个10000-65535之间的整数作为端口号,不用默认的3306,这样可以避免很多端口扫描。
port = 45619然后保存退出。
之后执行 systemctl restart mariadb 重启服务器。
这样我们就完成了数据库的配置,可以用本地工具测试连接。
5. Redis
很简单,因为没什么需要配置的。
也不需要开通外网访问。当然如果需要还是可以开一下的。
apt install redis-server6. Nginx
先安装一下。
apt install nginx打开 /etc/nginx/nginx.conf 配置文件。按照下面的例子配置其中的几行。
user www-data;
worker_processes auto; # [配置这个] 自动设置为 CPU 核心数
worker_rlimit_nofile 65535; # [配置这个] 工作进程能打开的文件描述符数量
events {
worker_connections 65535; # [配置这个] 每个工作进程的最大连接数
multi_accept on; # [配置这个] 允许一次接受多个连接
}
http {
client_max_body_size 100M; # [配置这个] 允许上传的最大文件大小
client_body_buffer_size 128k; # [配置这个]
}配置完之后运行
nginx -t这会进行配置文件的检查。如果没有报错,接下来就重启nginx:
systemctl restart nginx来让配置生效。
后面还有很多配置nginx的地方,基本上配置完之后都需要这样来一下检查和重启,之后就不再讲述如何检查nginx配置文件和重启生效的方式。
7. TMUX
很多系统可能已经预装了tmux,没有的话先安装一下。
我们之后用这个来运行开发服务器。
apt install tmuxtmux的基本使用方法:
- 使用
tmux new -s <session>建立新窗口。 - 使用完之后按
Ctrl+bd退出tmux而不关闭它。 - 之后我们使用
tmux ls查看打开了哪些窗口。 - 使用
tmux attach -t <session>连接回这个backend窗口 - 在tmux窗口内部使用exit,或在外部使用
tmux kill-session -t <session>关闭窗口。
好了,基本环境配置就到这里了。
建立工作目录并启动开发服务器
找个合适的地方拉取代码库,这将成为之后主要的工作目录。
我这里用的是 ~/Workspace/ccxc 目录。
配置 Git
首先记得配置 Git。这包括生成 ssh key,并把公钥复制到 Github。以及 Git 的默认用户名和 E-mail 的设置。此处略。
拉取代码库
把所有代码都 git clone 下来。
git clone git@github.com:cipherpuzzles/ccxc-backend.git
git clone git@github.com:cipherpuzzles/ccxc-admin.git
git clone git@github.com:cipherpuzzles/ccxc-sync-server.git
git clone git@github.com:cipherpuzzles/ccxc-website.git
git clone git@github.com:cipherpuzzles/ccxc-puzzle.git启动 Backend
首先建立一个新的 tmux 窗口
tmux new -s backend我们在新打开的终端里进入backend的目录。然后使用dotnet命令行运行一下。
cd ~/Workspace/ccxc/ccxc-backend
dotnet run --project ./ccxc-backend/ccxc-backend.csproj第一次运行需要等一段时间自动完成依赖包的下载。
然后会报错并自动退出,这是正常的。在这个过程中,backend会将设置模板释放到 Config 目录。
接下来我们打开这个配置文件:
cd Config
vim ccxc.config.toml修改必要的配置。下面这些是我建议改的:
DbConnStr = "" # 数据库连接字符串必须要改,不然就会出现第一次运行那样的立即自动退出。
ImageStorage = ""
ImagePrefix = ""
PassHashKey1 = ""
PassHashKey2 = ""
EnableEmailVerify = false为了保证之后的SSO可以进行,我们需要稍微修改一下代码。这个逻辑后续将做到配置文件里。
打开:./ccxc-backend/Controllers/Users/SsoController.cs
在第44行,修改或者加入一个自己的域名后缀。然后保存退出。
修改完之后重新运行一下dotnet run --project ./ccxc-backend/ccxc-backend.csproj
我们可以看到backend已经正常的运行了。
按下 Ctrl+b d 从这个tmux窗口里出来。
为 backend 和 static 配置 nginx
我们的目标是通过域名访问,虽然我们运行的都是开发服务器,但是在外部来看,看上去就像是一个普通的服务器。
在这次部署中,我为系统选择的主域名是 ccxc.local
所以这次我们需要配置的是 api.ccxc.local 和 static.ccxc.local 两个域名。
域名均使用 Cloudflare 作为CDN服务器,由 Cloudflare 为其自动颁发边缘证书,至于服务器,我们使用 Cloudflare 签发的超长有效期服务器证书来实现全程加密。
我们将证书和密钥文件分别存放在/etc/nginx/cert.crt和/etc/nginx/cert.key下。
我们先在 /etc/nginx/sites-available 目录下建立 backend.conf 文件。然后填入下面的内容:
sync的服务我们还没部署,不过这里出现在配置文件中了,我们一并配好,免得之后还要回来修改。
# WebSocket 连接升级配置
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# 后端服务负载均衡
upstream ccxc_backend {
server 127.0.0.1:52412;
keepalive 64;
}
upstream ccxc_sync {
server 127.0.0.1:15562;
keepalive 64;
}
# API 服务器配置
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name api.ccxc.local;
# SSL 证书配置
ssl_certificate /etc/nginx/cert.crt;
ssl_certificate_key /etc/nginx/cert.key;
# SSL 优化配置
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
# WebSocket 代理(同步服务)
location /ws-api {
proxy_pass http://ccxc_sync;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# API 代理(主后端)
location / {
proxy_pass http://ccxc_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}然后再建立一个 static.conf,内容如下:
# 静态文件服务器配置
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name static.ccxc.local;
# SSL 证书配置
ssl_certificate /etc/nginx/cert.crt;
ssl_certificate_key /etc/nginx/cert.key;
# SSL 优化配置
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
# 静态文件根目录
root "/var/www/static.ccxc.local";
index index.html;
# CORS 配置
location / {
# 处理 OPTIONS 预检请求
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'Content-Type, User-Token, X-Auth-Token';
add_header Access-Control-Max-Age 1728000;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
# 添加 CORS 头
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'Content-Type, User-Token, X-Auth-Token';
# 缓存配置
expires 1y;
add_header Cache-Control "public, immutable";
}
}接下来我们需要切换到 /etc/nginx/sites-enabled 目录,将之前两个配置文件链接到该目录下。
ln -s /etc/nginx/sites-available/backend.conf /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/static.conf /etc/nginx/sites-enabled/之后重启nginx让它生效。
启动同步服务器
我们还是从建立一个新的tmux窗口开始。
tmux new -s sync进入同步服务器的安装目录,然后使用npm install安装依赖包。
cd ~/Workspace/ccxc/ccxc-sync-server
npm install在运行之前我们先复制配置文件:
cp config.default.json config.json然后打开config.json编辑。一般来说这个配置文件不需要修改。apiRoot的唯一用途是调用/heartbeat-inner接口,是一个内部调用,因此你可以安全的使用http://localhost:52412进行(除非你修改了backend的默认端口)。
然后使用下面的命令启动
npm run start启动后就可以输入 Ctrl+b d 退出这个 tmux 窗口了。
启动后台管理面板
还是从新建一个tmux窗口开始
tmux new -s admin进入管理面板的前端代码目录,使用npm install安装所需依赖
cd ~/Workspace/ccxc/ccxc-admin
npm install在运行之前,我们需要打开并编辑 .env.development 文件。修改VITE_BACKEND_ROOT变量。
这里必须使用外网可以访问的地址。
VITE_BACKEND_ROOT=https://api.ccxc.local/api我们还需要修改一下 vite.config.js
export default defineConfig({
server: {
port: 10007, //固定一个端口
strictPort: true, //禁止devServer自动切换端口
hmr: {
protocol: 'wss',
host: 'admin.ccxc.local',
clientPort: 443
}
}
})按照上面的写法修改 server 这部分的设置。
然后我们启动开发服务器:
npm run dev之后输入 Ctrl+b d 退出 tmux 窗口。
为 admin 面板配置nginx
在 /etc/nginx/sites-available 目录下建立 admin.conf 配置文件。
# API 服务器配置
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name admin.ccxc.local;
# SSL 证书配置
ssl_certificate /etc/nginx/cert.crt;
ssl_certificate_key /etc/nginx/cert.key;
# SSL 优化配置
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
# API 代理(主后端)
location / {
proxy_pass https://127.0.0.1:10007;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 开发环境设置
proxy_read_timeout 1h;
proxy_send_timeout 1h;
proxy_buffering off;
}
}ln -s /etc/nginx/sites-available/admin.conf /etc/nginx/sites-enabled/之后重启nginx服务器。
启动网站主页
仍然从新建一个tmux窗口开始
tmux new -s website进入管理面板的前端代码目录,使用npm install安装所需依赖
cd ~/Workspace/ccxc/ccxc-website
npm install之前安装过ccxc-admin的依赖,这里应该很快就能安装成功了。
在运行之前,我们需要打开并编辑 .env.development 文件。修改VITE_BACKEND_ROOT变量。
这里必须使用外网可以访问的地址。
VITE_BACKEND_ROOT=https://api.ccxc.local/api我们还需要修改一下 vite.config.js
export default defineConfig({
server: {
port: 13880, //固定一个端口
strictPort: true, //禁止devServer自动切换端口
hmr: {
protocol: 'wss',
host: 'www.ccxc.local',
clientPort: 443
}
}
})按照上面的写法修改 server 这部分的设置。
然后我们启动开发服务器:
npm run dev之后输入 Ctrl+b d 退出 tmux 窗口。
为 website 面板配置nginx
在 /etc/nginx/sites-available 目录下建立 website.conf 配置文件。
这个配置区别和admin.conf很小,可以复制admin.conf来修改。
由于www.ccxc.local的特殊性,我们还增加了一段配置,让ccxc.local自动跳转过去。
# API 服务器配置
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.ccxc.local;
# SSL 证书配置
ssl_certificate /etc/nginx/cert.crt;
ssl_certificate_key /etc/nginx/cert.key;
# SSL 优化配置
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
# API 代理(主后端)
location / {
proxy_pass https://127.0.0.1:13880;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 开发环境设置
proxy_read_timeout 1h;
proxy_send_timeout 1h;
proxy_buffering off;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name ccxc.local;
# SSL 证书配置
ssl_certificate /etc/nginx/cert.crt;
ssl_certificate_key /etc/nginx/cert.key;
# SSL 优化配置
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
return 301 https://www.ccxc.local$request_uri;
}ln -s /etc/nginx/sites-available/website.conf /etc/nginx/sites-enabled/之后重启nginx服务器。
启动答题页面
终于到最后一个了
还是从新建一个tmux窗口开始
tmux new -s puzzle进入管理面板的前端代码目录,使用npm install安装所需依赖
cd ~/Workspace/ccxc/ccxc-puzzle
npm install打开并编辑 .env.development 文件。修改VITE_BACKEND_ROOT变量。
这里必须使用外网可以访问的地址。
VITE_BACKEND_ROOT=https://api.ccxc.local/api然后还是需要修改一下 vite.config.js
export default defineConfig({
server: {
port: 44565, //固定一个端口
strictPort: true, //禁止devServer自动切换端口
hmr: {
protocol: 'wss',
host: 'puzzle.ccxc.local',
clientPort: 443
}
}
})按照上面的写法修改 server 这部分的设置。
然后我们启动开发服务器:
npm run dev之后输入 Ctrl+b d 退出 tmux 窗口。
为 puzzle 面板配置nginx
在 /etc/nginx/sites-available 目录下建立 puzzle.conf 配置文件。
这个配置区别和admin.conf很小,可以复制admin.conf来修改。
# API 服务器配置
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name puzzle.ccxc.local;
# SSL 证书配置
ssl_certificate /etc/nginx/cert.crt;
ssl_certificate_key /etc/nginx/cert.key;
# SSL 优化配置
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
# API 代理(主后端)
location / {
proxy_pass https://127.0.0.1:44565;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 开发环境设置
proxy_read_timeout 1h;
proxy_send_timeout 1h;
proxy_buffering off;
}
}ln -s /etc/nginx/sites-available/puzzle.conf /etc/nginx/sites-enabled/之后重启nginx服务器。
初始化
接下来从前端进入主站 https://www.ccxc.local 并注册第一个帐号。
一般来说,会获得UID: 1
然后运行下面的代码来进行initadmin操作:
dotnet run --project ./ccxc-backend/ccxc-backend.csproj -- initadmin然后打开管理面板,进入 https://admin.ccxc.local/userbackend。
这会自动触发SSO。不过目前的SSO地址还是默认的ccxc.ikp.yt,所以是不可能成功的。我们手工将前面的部分修改成 https://www.ccxc.local/user/sso,然后按回车。
这样就能正常打开后台管理面板了。我们先进入“高级”-“系统设置”。将这里的:“项目前端地址前缀”“题目独立前端前缀”“题目Websocket地址前缀”都修改一下。这样之后就能正常进入了。
连接远程开发工具
连接 Codex
在本文写作的时候,Codex刚刚更新了SSH功能,目前,这个功能还需要手工打开,未来应该不需要这一步。
打开Codex的配置文件:~/.codex/config.toml
添加下面这一段:
[features]
remote_connections = true保存后,重启 Codex 应用。
连接 VSCode Remote
一般来说,我们会用VSCode Remote来连接服务器远程编写代码。这个配置也很简单就略过了。
配置访问权限
其实到这里,服务器本身已经可以使用了,我们可以建立类似正式服那样的账号系统和权限机制,防止未授权用户获取内容。
不过我们还是更进一步,使用 Cloudflare ZeroTrust,让未授权的用户完全无法获取任何内容。
