不知道写什么,干脆水一发小白向的 ChatGPT Web Share 搭建拼车服务教程吧,本文建立在已拥有一个 ChatGPT Plus 账号的基础上,购买方式可在论坛搜素。
- 选购主机
要部署拼车站点,首先需要一台属于合适的服务器(VPS),主要需注意三点:
- 性能
- 网络
- 价格
性能方面,我们以一张 IDC 价目表( VPS 商家)为例。
IDC一般会提供的信息包括: - CPU 核心数
- RAM(内存)
- DISK(硬盘容量)
- 流量和带宽
- 价格
- 线路
- ……
这里我们主要关注 CPU 和内存两项资源即可。CWS 文档的推荐配置为 2 核 2 G RAM以上,最低配置为 1 核 1 G ,我们根据该标准选取合适款式的 VPS 即可。
快速上手 | ChatGPT Web Share 文档)
另外,针对于我们想要搭建的 CWS 服务,新手在选购中还需注意的点可能有: - 支付方式
- 网络线路
- 主机位置
支付方式:IDC 商家并不一定提供微信和支付宝这两项国内最常用的支付渠道,尤其是外国商家,所以在选购时需要挑选支持自己意向支付渠道的商家;
网络线路:为了拥有良好的连接体验,需要在购买前了解所选 VPS 的网络线路,很多 IDC 都会提供 Looking Glass 测速服务,可以让自己和拼车小伙伴提前测试,关于线路的详细信息可参考下面主题(若不差钱则选购三网 CN2 GIA 即可);
https://linux.do/t/topic/36472?u=ginkgo
主机位置:中国大陆地区的主机提供 Web 服务需要进行 ICP 备案。
另外,购买服务器时可以将其安装为 Debian 12 或 Ubuntu 22 系统,前者相对精简,后者会带更多默认软件,笔者推荐前者。
- 部署服务
硬件都已置办完毕,接下来就是软件的搭建了。
连接服务器
作为从未接触过 Linux VPS 的小白,首先需要一个 SSH 软件来连接上我们的 VPS 。这里以开源免费的 WindTerm 来演示(其实我喜欢 Termius ),关于 SSH 工具的选取可参考下列主题:
到下面的网站下载最新发行版(根据自身系统选择):
这里为方便读者,贴出 2.6.1版本(截至 2024 年 4 月 14 日的最新版)下载链接(发行说明太长了):
- Linux 系统
https://github.com/kingToolbox/WindTerm/releases/download/2.6.0/WindTerm_2.6.1_Linux_Portable_x86_64.tar.gz - Mac 系统
https://github.com/kingToolbox/WindTerm/releases/download/2.6.0/WindTerm_2.6.1_Mac_Portable_x86_64.dmg - Windows系统( 32 位)
https://github.com/kingToolbox/WindTerm/releases/download/2.6.0/WindTerm_2.6.1_Windows_Portable_x86_32.zip - Windows系统( 64 位,如果你的电脑内存大于 3 GB就选这个)
https://github.com/kingToolbox/WindTerm/releases/download/2.6.0/WindTerm_2.6.1_Windows_Portable_x86_64.zip
解压缩之后打开软件,点击左上角“会话“——”新建会话“,得到下方对话框
“主机”处填写你从 IDC 那里获取的服务器 IP 地址;
“端口”处一般保持默认的 22 端口,除非你买的是 NAT 服务器,但是不建议新手尝试。
输入完毕之后,点击“连接”,会弹出验证对话框,点击“账户”,输入“root“,再输入 IDC 提供的密码,就成功连接上你的服务器啦!
如果不能成功连接,可能是由于众所周知的原因,你需要检查服务器是否在黑名单内,通过下面的网站进行多地 Ping :
https://www.itdog.cn/ping/
如果你的服务器在中国大陆一片红,海外却一片绿,说明不幸被墙,则需要更换 IP ,可以发工单联系客服或自主更换 IP 。
Docker 安装
Docker 是一个开源的应用容器引擎,大佬们会使用 Docker 技术封装软件,我们便可以通过简单的配置开箱即用。
我们参考 Docker 官方文档进行安装
首先将 Docker 添加到 apt 源,apt 是一个包管理器,可以看作一个软件商城,通过它来进行软件的下载和更新。
将下面的代码复制到你的 SSH 工具中并回车:
添加 Docker 的官方 GPG 密钥:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
添加仓库到apt源:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
上面的代码已经将 Docker 作为软件提供商添加到了我们的 apt 仓库,接下来我们键入命令安装 Docker 软件:
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
验证安装成功:
sudo docker run hello-world
当你看到”Hello from Docker!“字样的文本,说明安装成功了!
ChatGPT Web Share 部署
安装了 Docker 软件之后,我们便可以部署拼车服务了,首先创建一个单独的文件夹用于数据存放:
cd ~
mkdir cws && cd cws
mkdir -p data/config
再将代码写入环境变量:
export MONGODB_PASSWORD=password # MongoDB 密码
export INITIAL_ADMIN_PASSWORD=password # 初始管理员密码
MongoDB 是 CWS 所使用的数据库密码,管理员密码是拼车面板的管理员密码,都需要进行修改,不要使用默认密码。
再执行下面的命令生成配置文件:
docker run -it --rm \
-v $PWD/data/config:/tmp/config \
ghcr.io/chatpire/chatgpt-web-share:latest \
python /app/backend/manage.py create_config -O /tmp/config --generate-secrets --mongodb-url "mongodb://cws:${MONGODB_PASSWORD}@mongo:27017" --initial-admin-password "${INITIAL_ADMIN_PASSWORD}" --chatgpt-base-url http://ninja:7999/backend-api/
接着创建 .env 文件:
echo "TZ=Asia/Shanghai" > .env
echo "MONGO_INITDB_DATABASE=cws" >> .env
echo "MONGO_INITDB_ROOT_USERNAME=cws" >> .env
echo "MONGO_INITDB_ROOT_PASSWORD=$MONGODB_PASSWORD" >> .env
再创建 Docker Compose 配置文件:
vi docker-compose.yml
通过 vi 编辑器创建文件后,按下“Ins Scrlk”键进入插入模式,粘贴下列内容:
services:
chatgpt-web-share:
image: ghcr.io/chatpire/chatgpt-web-share:latest
container_name: cws
restart: unless-stopped
ports:
- 5000:80
volumes:
- ./data:/app/backend/data
environment:
- TZ=${TZ}
- CWS_CONFIG_DIR=/app/backend/data/config
depends_on:
- mongo
networks:
- cwsnetwork
mongo:
container_name: mongo
image: mongo:6.0
restart: always
# ports:
# - 27017:27017
volumes:
- ./mongo_data:/data/db
environment:
MONGO_INITDB_DATABASE: ${MONGO_INITDB_DATABASE}
MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
networks:
- cwsnetwork
networks:
cwsnetwork:
name: cwsnetwork
external: true
最后按下键盘的”ESC“键盘退出编辑,按下“:"(冒号键),输入“wq"再“回车”保存。( Vim 编辑器的用法可参考下面的文章)
接着启动服务,执行:
docker compose up -d
如果不起作用,则执行:
docker-compose up -d
访问”http://服务器IP:5000/",如果正常看到网页,说明服务部署成功了,接下来我们需要进行 CWS 服务的配置。
- 服务配置
输入你的管理员账号密码登录(默认账号admin),点击右上角的小人标识进入管理后台,点击左栏的小齿轮进入系统配置,将项目下的 Enabled 和 Is Plus Account 都打开,在“Chatgpt Base Url”项下输入始皇的接口“there is no wall - 搞七捻三 - LINUX DO
接着在上面切换到“credentials”项目,这里需要输入 Access Token,在 ChatGPT 官网登录账号后访问:
https://chat.openai.com/api/auth/session
"accessToken"冒号后,所有引号内的字符均是 Access Token (不含引号),复制过去保存即可。现在,再启用各类模型,并在左侧“用户管理”界面中设置各账号的模型权限,就可以开始与 ChatGPT 对话啦!
如果你不想配置域名、https 和 CDN,便可以止步于此,如果你想要给它加上 https 安全锁,而且也不想要地址中端口号的累赘,想要直接通过域名访问服务,并添加 CDN 加速,请继续阅读。
- 采购域名(可选)
为了方便访问,并部署 SSL 支持(给你的网站加上https的小锁,可以保护内容不被中间人获悉和篡改)和隐藏源服务器 IP(有好事之徒可能会通过 DDos 等方式打你的服务器),我们需要购买一个域名。
自从 Freenom 停止 tk 等免费一级域名注册后,已很难找到免费域名了,如果你实在不愿意花这笔钱可以参考下面的主题:
一般推荐采购一个一级域名,可以使用下面的域名比价网站
https://tld-list.com/
左边输入你想要的域名,例如“linux”,右边会显示目前各顶级域下“linux”的注册情况,右侧显示了该域名是否被注册、最便宜的注册价(首年)、最便宜的续费家、最便宜的转移价以及最便宜的三年总价。
目前来说,较为实惠的域名是6-9位纯数字 xyz 域名,例如“123456789.xyz”,比较便宜的注册商有如下两家
https://www.spaceship.com/
https://www.namesilo.com/
在前者注册,十年仅需 6.99 美元( 50.59 人民币),可谓是相当经济实惠了。
- 设置域名(可选)
为了使用大善人 Cloudflare 提供的 CDN 等各类服务,我们需要将域名的 DNS 服务商从默认的 Spaceship 转移到 Cloudflare ,首先你需要注册一个 Cloudflare 账号,在控制台主页点击“添加站点”,输入你的域名,选择最下面的免费套餐,DNS 记录可以先点击”继续“跳过,复制分配给你的两个“Cloudflare名称服务器”。
https://dash.cloudflare.com/
登录你的 Spaceship 账号,访问高级 DNS 设置界面:
https://www.spaceship.com/application/advanced-dns-application/
点击想要配置的域名,在“Custom DNS”一栏中点击“change",输入你刚刚复制的两个 Cloudflare 名称服务器地址,并保存。
这样就成功地把你的域名托管到了 Cloudflare,点击“立刻检查名称服务器”并等待其生效即可,DNS 记录的传递需要时间。
- 内网穿透(可选 / 推荐)
推荐使用大善人 Cloudflare 推出的 Zero Trust 服务中的 Tunnel 来进行内网穿透,安全地将内网Web服务映射至公网。使用方法可参考该主题:
记得把反代端口,即 Service 项下的 URL 修改为localhost:5000
,如果是通过容器方法部署,将 cloudflared 并入 CWS 的 Docker Compose 配置,也可反代 cws:80
。成功启动之后访问你设置的域名即可打开服务。
- 反向代理(可选)
我们也可以通过 Nginx、Caddy 等软件进行反向代理,这里以Nginx为例。
首先安装Nginx,这里推荐烧饼博客打包的 Nginx 镜像源,已整合了 QUIC / HTTP3 和 Brotli 等实用插件,执行下方命令即可安装。
apt install -y lsb-release ca-certificates apt-transport-https curl gnupg dpkg
curl -sS https://n.wtf/public.key | gpg --dearmor > /usr/share/keyrings/n.wtf.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/n.wtf.gpg] https://mirror-cdn.xtom.com/sb/nginx/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/n.wtf.list
apt update
apt install -y nginx-extras
然后修改 Nginx 配置,我这里提供一份范本,参照修改即可,先删除默认的配置,再进行编辑。
rm /etc/nginx/nginx.conf
vim /etc/nginx/nginx.conf
下方的范本已添加 HTTP2 和 HTTP 3 协议的支持,开启了 0 - RTT ,启用了 Gzip 和 Brotli 压缩,为静态资源添加了缓存标头,为 HTTP 添加跳转并禁止 IP 访问。仅需要将各处的 server_name
修改为自己域名(配置中均以 cws.linux.do 为例)便可把服务跑起来,当然你也可以使用 Nginx 配置生成器 来生成配置。另外,根据服务器 CPU 性能不同,也可调整 gzip_comp_level
和 brotli_comp_level
的值,前者介于【 1,9 】,后者介于【 0,11 】,数值越大压缩等级越高,所需 CPU 性能也越大。可以通过 top
命令观察 Nginx 的 CPU 占用,与此同时访问网站以确定此值。
user www-data;
pid /run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 65535;
# Load Modules
include /etc/nginx/modules-enabled/*.conf;
events {
multi_accept on;
worker_connections 65535;
}
http {
charset utf-8;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
log_not_found off;
types_hash_max_size 2048;
types_hash_bucket_size 64;
client_max_body_size 16M;
autoindex off;
# MIME
include mime.types;
default_type application/octet-stream;
# Logging
access_log off;
error_log /dev/null;
# SSL
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# Mozilla Modern Configuration
ssl_protocols TLSv1.3;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 9.9.9.9 149.112.112.112 64.6.64.6 64.6.65.6 valid=60s;
resolver_timeout 2s;
# Connection Header for WebSocket Reverse Proxy
map $http_upgrade $connection_upgrade {
default upgrade;
"" close;
}
map $remote_addr $proxy_forwarded_elem {
# IPv4 addresses can be sent as-is
~^[0-9.]+$ "for=$remote_addr";
# IPv6 addresses need to be bracketed and quoted
~^[0-9A-Fa-f:.]+$ "for=\"[$remote_addr]\"";
# Unix domain socket names cannot be represented in RFC 7239 syntax
default "for=unknown";
}
map $http_forwarded $proxy_add_forwarded {
# If the incoming Forwarded header is syntactically valid, append to it
"~^(,[ \\t]*)*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*([ \\t]*,([ \\t]*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*)?)*$" "$http_forwarded, $proxy_forwarded_elem";
# Otherwise, replace it
default "$proxy_forwarded_elem";
}
# Load Configs
include /etc/nginx/conf.d/*.conf;
# ChatGPT Web Share
server {
listen 443 quic reuseport;
listen 443 ssl;
listen [::]:443 quic reuseport;
listen [::]:443 ssl;
http2 on;
http3 on;
http3_hq on;
server_name cws.linux.do;
# SSL
ssl_ecdh_curve X25519;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_early_data on;
quic_retry on;
ssl_certificate /etc/nginx/ssl/cws.crt;
ssl_certificate_key /etc/nginx/ssl/cws.key;
# Security Headers
add_header Alt-Svc 'h3=":443"; ma=86400';
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always;
add_header Permissions-Policy "interest-cohort=()" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# . Files
location ~ /\.(?!well-known) {
deny all;
}
# Logging
access_log /var/log/nginx/access.log combined buffer=512k flush=1m;
error_log /var/log/nginx/error.log warn;
# Reverse Proxy
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
# Proxy SSL
proxy_ssl_server_name on;
# Proxy Headers
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Forwarded $proxy_add_forwarded;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header Early-Data $ssl_early_data;
proxy_set_header Accept-Encoding "";
# Proxy Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Cache
location ~* \.(css|js|woff|woff2|ttf|webp|png|jpg|jpeg|gif|svg|ico)$ {
proxy_pass http://127.0.0.1:5000;
expires 1y;
access_log off;
add_header Cache-Control "public";
proxy_ssl_server_name on;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Forwarded $proxy_add_forwarded;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header Early-Data $ssl_early_data;
}
# favicon.ico
location = /source/favicon.ico {
access_log off;
log_not_found off;
}
# robots.txt
location = /robots.txt {
log_not_found off;
}
# Gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/x-javascript application/rss+xml application/xml application/atom+xml image/svg+xml image/x-icon image/vnd.microsoft.icon image/bmp;
# Brotli
brotli on;
brotli_comp_level 6;
brotli_buffers 16 8k;
brotli_min_length 20;
brotli_types text/plain text/css text/xml text/javascript application/json application/javascript application/x-javascript application/rss+xml application/xml application/atom+xml image/svg+xml image/x-icon image/vnd.microsoft.icon image/bmp;
brotli_static always;
}
# HTTP redirect
server {
listen 80;
listen [::]:80;
server_name .cws.linux.do;
return 301 https://cws.linux.do$request_uri;
}
# Ban IP Request
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name "";
return 444;
}
}
不过可别忘了,我们还没有申请 SSL 证书,以及生成前向保密所需的 dhparam
参数,让我们使用 acme.sh 来申请 SSL 证书,这里通过 DNS API 验证的方式来快捷地生成,首先登录 Cloudflare 控制台,点击右上角的小人图标进入“个人资料”页面,进入左侧“API令牌”栏目,点击“ Global API Key ”右方的“查看”,输入密码查看你的全局 API 密钥,千万不要泄露这个密钥!
接着执行脚本申请 SSL 证书(假定你的域名已托管到 Cloudflare ),下方代码中的【[email protected]】需要替换为自己的邮箱,密钥和邮箱也要替换,域名需替换为自己的域名,这里都以【linux.do】为例。
curl https://get.acme.sh | sh -s [email protected]
acme.sh --set-default-ca --server zerossl
export CF_Key="前面记录的 Cloudflare API Key"
export CF_Email="你的 Cloudflare 绑定邮箱"
acme.sh --issue \
--dns dns_cf \
-d linux.do -d \*.linux.do \
--keylength ec-256
上面的代码主要进行了如下工作:
- 安装 acme.sh ;
- 替换默认 CA 为 ZeroSSL ;
- 输入 Cloudflare 账号的相关环境变量;
- 为【linux.do】域名申请 ECC-256 类型的泛域名证书。
接着再安装 SSL 证书到 Nginx 目录,下面的安装目录与前述 Nginx 配置中的目录和文件名是匹配的,仅需修改域名。不要手动复制证书!那样无法自动更新!
acme.sh --install-cert -d linux.do \
--key-file /etc/nginx/ssl/cws.pem \
--fullchain-file /etc/nginx/ssl/cws.pem \
--reloadcmd "nginx -s reload"
为了实现前向安全,我们还需要生成 DH 参数,下面的命令将生成一个 4096 位的 DH 参数,需要运行一段时间。
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096
接下来,重启 Nginx 更新配置,即可享受兼顾安全与高效的网站了。
nginx -t
nginx -s reload
- 安全加固(推荐)
公网上每天都有无数脚本在扫描默认端口的服务器,为了提高 SSH 服务的安全新,我们做一些简单的安全配置即可阻挡大部分自动化攻击。
- 修改默认端口
- 使用密钥登录
访问 SSH 服务的默认配置文件路径
vim /etc/ssh/sshd_config
删去 #Port 22
前的注释,并在下面添加一行新端口(千万不要直接修改端口,万一出问题你就连不上服务器了!),形成下面的结构,最好选取一个高位端口,端口号必须介于【 1,65535 】之间。
Port 22
Port 6324
systemctl restart sshd
先不要关闭现有的对话,另建一个使用新端口进行连接的对话,确定新端口可以访问后再删去默认端口并重启 SSH 服务 systemctl restart sshd
。
为了配置密钥登录,我们先生成一个 SSH 密钥对,助记词( passphrase )自行设置,这是你丢失私钥时登录的方法,不推荐留空。
mkdir -p ~/.ssh && cd ~/.ssh
ssh-keygen -t ed25519
cd .ssh
cat id_ed25519.pub >> authorized_keys
chmod 600 authorized_keys
chmod 700 ~/.ssh
将私钥文件 id_ed25519
下载或复制其内容到本地 cat /root/.ssh/id_ed25519
。
再打开 SSH 配置文件,确认 PubkeyAuthentication yes
设置为启用,并且前面无#注释。 systemctl restart sshd
重启 SSH 服务后,通过 SSH 终端导入私钥测试能否使用密钥登录,确认可行后禁止密码登录 PasswordAuthentication no
,并再次重启 SSH 服务。