最近在部署自己的站点,有很多工作之前都做过,但总是记不清细节是什么了,折腾半天。为了避免以后可能发生的重复劳动,我准备把最佳实践总结下。因为这些实践面对的都不是大型互联网项目,而是自己的Side Project,所以不需要比如Kubernetes或者Spring Cloud一样的技术,容器也用不上,主打一个传统+够用。
本文描述的运行环境基于阿里云,其他云平台其实都差不多。服务器运行在ECS一样的环境中,多台服务器共同存在于一个VPC也就是子网中。后端使用Spring Boot,当然其他生态的后端也行,反正最后都是提供一个端口对外。前端使用Vue/React生态,如果使用SSR,那么也需要服务器。数据库选择的是PostgreSQL,没用云服务商的RDS服务,不是因为和云平台的耦合度较高,而是因为价格比较贵。
网站结构
我们简单描述一个网站的构成,它有以下组成部分:
- 网关服务器,用来转发流量,有公网IP
- 开发环境服务器(前端+后端+数据库),平常联调
- 生产环境服务器(前端+后端+数据库),正式服务
乍一看,怎么都需要7台服务器了,还没上线呢,怎么就这么多开销?其实不用。
理论上,你有多少台就有多少台的部署方式,比如这几种:
- 一台(所有的放一起)
- 两台(网关+其他)
- 三台(网关+开发+生产)
- 四台(网关+后端+前端+数据库)
反正这些东西都是放在同一个VPC里的,怎么组合都行。如果你的前端是放在OSS里的,前端也可以省了。
刚开始各个服务器可以是低配的,比如1核1G这种。以后如果流量增大了,可以扩充成单独的7台。对于无状态的前后端服务器,当生产环境流量继续增大,可以纵向增强单台机器的配置,也可以横向搞多台服务器负载均衡。对于有状态的数据库来说,可以不断扩展内存和磁盘规格,最后实在不行还可以切换到RDS上,在此基础上还有上升的空间。
然后呢,还是不行怎么办呢?当然是打开懂车帝选车呀!想象下你的10台32核64G的服务器都跑满的时候,那你就发了呀,光服务器费用都要3w一个月了,那你一个月不得挣个10w块?那不得换个福特烈马大沼泽版本?
可惜对于我这种半路出家的Java后端,接触的系统都是厂里内部的管理系统,1QPS你受得了么。关于大流量场景下的业务处理,我还真是没有实操过,跟面试官吹牛逼的时候也是纸上谈兵。但换个角度来说,作为个体户,你已经不需要去钻研那些东西了,很多赚钱的小公司用的都是一些非常传统和草台的技术。我不是说让你降低技术的追求,而是节省时间,更多关注于实现业务。
扯远了,我们接下来从网关开始。
网关入口
网站对外有统一的入口,就是搞一台nginx做反向代理和负载均衡,同时做SSL卸载。把流量转到内部,内部的服务器不暴露到外网,提高安全性。对外只暴露80和443端口。
nginx的部署
我们使用的系统是Ubuntu 24.04,它是LTS版本。
首先是安装nginx,安装完成之后,它会自动跑起来并在80端口提供一个页面:
sudo apt install nginx
它的站点配置主要存在于两个文件夹:
/etc/nginx/sites-available
/etc/nginx/sites-enabled
默认情况下,两个目录下都有个名字为default
的文件,后者是前者的符号链接。从名字就可以看出,如果某个站点你需要下线,将站点配置从/etc/nginx/sites-enabled
删除就行。
nginx作为一个守护进程在后台运行,service
命令和systemctl
命令都可以控制nginx的启停,前者是对后者的包装,在Ubuntu引入systemd
之前,servcie
包装的是/etc/init.d
里的脚本。因为我是十来年的Ubuntu用户,所以还是喜欢老的用法:
service nginx start
启动nginxservice nginx restart
重启nginxservice nginx reload
重新加载配置service nginx status
查看nginx状态
如果你增减了站点或者修改了配置,可以通过重启或者重新加载配置来查看效果。
SSL证书的管理
在进行具体的nginx配置之前,我们先把SSL证书先申请一下,假设我们有以下域名:
example.com
dev.example.com
api.example.com
api-dev.example.com
分别是前端和后端的域名,包括线上环境和开发环境。请确保在你的域名服务商哪里,把这几个域名的DNS解析,都设置到你的网关IP上。然后通过certbot
命令来申请letsencrypt证书:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx certonly -d [域名] --agree-tos -m [邮箱]
它会利用现有的nginx,来接受CA对于域名的访问,以确定你的域名所有权。最后你会收到证书,存放在:
/etc/letsencrypt/live/[域名]/fullchain.pem
证书/etc/letsencrypt/live/[域名]/privkey.pem
私钥
这个证书的有效期是3个月,本来我还打算搞个定时任务,快到期的时候更新证书,但是没想到看到一个输出:
Certbot has set up a scheduled task to automatically renew this certificate in the background.
呀,原来certbot
会自己更新,这不挺好,一次申请,终身更新。我们只要重复几次就能得到所有域名的证书。如果你对通配符证书有需要,可以查看如何用DNS插件来获取证书。
证书更新了之后,还得告诉nginx,让它加载新的证书。不然它还是会用内存里的旧证书。certbot有内置的hook机制在更新证书之后,执行自定义脚本。脚本需要放在/etc/letsencrypt/renewal-hooks/deploy
目录。
比如我就新增了一个nginx-reload文件,通过chmod a+x nginx-reload
增加执行权限,内容为:
#!/bin/bash
service nginx reload
这样每次成功更新证书之后,就重新加载nginx,不用重启影响业务。
nginx的配置
首先,让http的流量转发到https。我们在/etc/nginx/sites-available
下新增一个文件redirect,配置为:
server {
listen 80;
return 301 https://$host$request_uri;
}
此配置就是将80端口的请求,重定向到https,接下来通过命令让站点生效:
sudo ln -s /etc/nginx/sites-available/redirect /etc/nginx/sites-enabled/
sudo service nginx reload
接下来搞定前后端服务器的4个站点的配置,内容都是类似的。举个后端服务器的例子:
upstream api {
server [API服务器IP]:8080; # 如果多个服务器,就多写几条,nginx会轮训转发
}
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
location / {
proxy_pass http://api;
}
}
你可以将其中的[API服务器IP]
替换为你的服务器内网IP,我已经将这个配置搞的极简了,它是能运行的最小化配置,你拷贝过去改改就能跑。你可能在其他地方看到各种配置参数,比如用proxy_set_header
把部分Header转发到实际的服务器上。我后续会再写一篇文章,讲讲用nginx做反向代理的时候,有哪些配置需要特别注意,以及负载均衡时的各种机制。
剩下的站点依次配置生效就可以了,反向代理工作已完成,也就是nginx的部分已经结束了,是不是很简单?
NAT网关
如果你的ECS没有公网IP,你是不能访问公网的,这一点很扯,为了解决这个问题,你可能需要一个NAT网关。可以直接将NAT网关配置在nginx服务器上,统一管理流量。为了更详尽地解释搭建的过程,我准备将这块内容专门独立整理成一篇文章,也是稍后放出。
数据库部署
数据按的部署没有难度,这里只是记录下PostgreSQL 17的部署流程,从头开始折腾的话还是挺费时间的。以下两种方式任选:
Docker部署
我经常在开发环境快速搭建PostgreSQL,一个命令就可以跑了:
docker run -d \
-e POSTGRES_USER=db-user \
-e POSTGRES_DB=db-name \
-e POSTGRES_PASSWORD=db-pass \
-p 5432:5432 \
--name pg-db \
--restart=always \
postgres:17
以上配置包含了很多关键参数,数据库名字、用户名、密码等等,你可以改成你自己的。如果你想跑多个实例,更换下端口重新执行就可以。--restart=always
参数保证此容器在重新之后继续执行。当你不需要的时候直接删除容器就行,方便快捷。
传统部署
记录下在Ubuntu 24.04安装PostgreSQL 17的过程。
Ubuntu 24.04自带的源里的PostgreSQL版本是16,如果要安装最新的版本,你需要使用PostgreSQL的官方APT源:
sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
感觉官方提供如此便捷的工具。接下来就可以安装了:
sudo apt install postgresql-17
然后用PostgreSQL的超级用户postgres来链接数据库:
sudo -u postgres psql
创建用户和数据库:
CREATE USER "db-user" WITH password 'db-pass'
CREATE DATABASE "db-name" OWNER "db-user"
因为我们要通过IP和端口,用账号密码链接数据库,所以要在/etc/postgresql/17/main/pg_hba.conf
中配置:
# IPv4 remote connections:
host all all 0.0.0.0/0 scram-sha-256
host
表示TCP/IP连接,两个all
表示所有数据库和所有用户,后面的IP段表示允许所有IP连接,因为数据库不暴露在外网,那么这就是允许所有的内网机器连接。最后一个scram-sha-256
表示用密码链接,名字比较奇怪哈,因为它用了SCRAM-SHA-256验证防止密码被嗅探,安全性较高。不过这还没完,pg_hba.conf
只是管理认证方式的,还得从物理上允许远程连接。
在/etc/postgresql/17/main/postgresql.conf
中修改一行:
listen_addresses = '*' # 原来是localhost
好了,PostgreSQL算是配置完了,重启就好:
sudo service postgresql restart
如果你还不能在内网成功访问到数据库,记得检查下ECS所在的安全组,有没有放开5432端口。
前后端的部署
我在考虑前后端如何部署的时候,总是想搞个流水线,github代码一合并,自动走流水线部署。有许多CI/CD方案是可以选择的,比如阿里云的云效、自建Jenkins、 GitLab CI/CD和Github Actions等等。但是因为项目都还没跑起来,我选择用万能的scp上传构建产物。
比如用Spring Boot写的后端,运行用下面这种方式运行:
nohup java -jar app.jar &
前端也会有类似的命令,比如NextJS可以这样跑:
nohup npm run start &
你可别笑我哈,我是感觉能运行就行,就算搞流水线最后执行的也都是这些东西。
上传全靠scp
,部署全靠nohup
,看日志全靠tail -f nohup.out
,监控全靠阿里云ECS控制台。
如果部署的次数足够多,我嫌烦了,可能会用云效,到时候我再把搭建流水线的过程总结出来。(后续:的确烦了,用云效搭建了Spring Boot后端的流水线)
小结
有了本文,下次我再搭建环境我就不用再去回忆了。你也可以依照这里的命令,快速完成目标。
最后修改于 2024-11-01