部署篇
这里是最最最激动人心的地方,终于要部署你千辛万苦写完的项目啦
这里部署的顺序,可以反着来,因为前端的部署一定情况下依赖于后端的部署,因此我们可以暂且先放眼于后端,因为操作的指令都是一样的,细节的区别再分别详细介绍
后端部署
数据库建表,服务端容器配置,这些内容暂时不属于笔记分享范围
后端程序需要打包或编译,以 java 为例
java 打包部署,因为本人技术有限推荐使用 jar 打包,减少部署的麻烦,如果你并不想把所有东西都打成 jar 包,可以去搜索学习一下依赖分离,和容器分离地打包方式,我仅使用fat jar 演示
手动部署
手动部署就是人工把服务端上传到服务器运行部署,虽然麻烦,且一旦出现问题每次都要重新打包上传,但是通过这个方法你可以熟练地掌握这些细节
服务端打包
虽然使用 jar 部署,但还是推荐把配置文件分离,这样可以直接在服务上修改配置后重新部署,减少手动上传的次数
TIP
关于为什么需要把配置文件分离?以上的理由算一个,还有一个理由:开发环境和部署环境是不一样的,有可能你需要把配置文件里的名称,或路径更改,那么分离配置文件可以灵活地做到及时修改
进入 idea
- 用 maven 打包,在 lifecycle 中选中
clean和jar运行
- 打包完成,在
target目录下,存放着我们打好的 jar 包了
TIP
其实你的配置文件已经被默认复制进了 jar 包里,但是没必要花精力去阻止它们被复制,我们只需要在服务器运行时,使用新的配置(application.yml 或 application.proerties)文件覆盖就可以了,详细请接着看。
好的开始是成功的一半,我们现在获得了一个 jar 和若干配置文件
上传服务器
现在我们需要把 jar 和配置文件上传到服务器上,服务间文件传输的协议一般为 sftp,我们可以选择的工具有很多,如果你喜欢使用xftp也没问题,更者你想先把文件传到 git 仓库,再从仓库下到服务器,都可以
这里我选择使用openssh包下自带的指令scp
scp a.tar.gz username@127.0.0.1:~/xx
# 表示把 a.tar.gz 文件拷贝到服务器 username 用户的家目录里的 xx 里
scp a b c username@127.0.0.1:~/xx
# scp 支持多文件拷贝,这行表示将 a b c 文件拷贝到服务器 username 用户的家目录里的 xx 里,~表示/home/username
scp -r ~/xx username@127.0.0.1:~/
# scp 支持文件夹直接拷贝,这行表示把本地当前用户家目录的 xx 文件夹拷贝到服务器 username 用户的家目录里
# 如果你想从服务器下载文件,可以使用
scp username@127.0.0.1:~/a.tar.gz ~/xx
# 即把服务器 username 用户家目录下的 a.tar.gz 文件下载到本地用户家目录下的 xx 里
TIP
当然username@127.0.0.1:path里的 path,不仅限于家目录,只要是目标用户有权限访问到的地方都可以拷贝到,若是不存在的文件夹,ssh 会帮忙创建的
将上一步得到的 jar 和配置文件上传到服务器上
好了,接下来直接到服务器上查看 
上传完成
配置服务端
登录服务器后,cd 进入服务端工作的目录
如果你之前被打入 jar 包的配置文件没有什么问题,此时完全可以直接执行java -jar ./xxxx.jar,运行服务端了。
但若要使用新的配置文件,可以执行java -jar xxx.jar --spring.config.location=绝对路径让 java 以绝对路径的配置文件执行
我推荐使用以下的方案: Spring 程序会按优先级从下面这些路径来加载 application.properties 配置文件
- 当前目录下的
/config目录 - 当前目录
- classpath 里的
/config目录 - classpath 跟目录
所以你可以直接把配置文件放在 jar 包目录下,或 jar 包目录下的config文件夹里
如果你为了不同环境设置了一个配置文件,请使用
application-前缀命名它们,例如:application-dev.yml这样做,你就可以在运行时直接执行java -jar xxx.jar --spring.config.location=dev进行选择配置文件执行
这里我们直接新建config文件夹,把配置文件放在里面
mkdir config
mv application.yml logback-spring.xml config/

好了,试试命令:java -jar xxxx.jar运行你的服务端

没有问题!
持续运行服务端
读到上面,你已经兴高采烈地启动后端了,可是又出现问题了,你只要断开 ssh,程序就被终止了
这是为什么呢?原来在 linux 中,window 和 session 是绑定在一起的,只要 session 结束,window 就关闭了,此时 window 中运行的非守护进程就会直接结束了,反之也成立,把 window 关闭后 session 也会被迫断开
因此让后端程序持续运行就有两个方案:
- 以守护进程来运行程序
- 将 window 和 session "解绑"
守护进程
守护进程和其他进程的区别是什么呢?守护进程其实并不是需要守护的进程(这么理解也没毛病),而是守护其他进程的进程,即直到其他所有进程都结束才会结束的进程
所以,可以使用守护进程运行服务端
nohup java -jar xxx.jar &
# nohup java -jar CodeSheep-1.0.1.jar
# nohup: ignoring input and appending output to 'nohup.out' 在该目录保存了一个.out 文件,它实时更新该进程的输出文字(保存日志都省了 bushi)
使用
tail -f nohup.out实时查看程序情况
如此一来,服务端就在后台以守护进程的方式执行了,现在我们关闭 ssh,服务端一样运行
那么如果要关闭程序呢?没有办法,你只能使用 kill pid 的方式来关闭程序
获取pid:
ps -ef | grep xxx | grep -v grep
# xxx 为你进程名称的关键词
# ps -ef | grep CodeSheep | grep -v grep
# mosquito 559004 538908 8 16:11 pts/3 00:00:10 java -jar CodeSheep-1.0.1.jar
这里第二个数字就是程序的pid, 我这里的 pid 是559004
关闭进程:
kill -9 559004
tmux
我推荐使用 tmux, 运行/管理你的进程和窗口
网上的教程很多,推荐 阮老师的 tmux 博客
半自动部署
可以使用 idea 部署,这个随便看个教程就知道了,其实无非也是配置好了 sftp,能更快捷地帮你传输文件而已,你需要更精细地活,还是要亲自操作
前端部署
web 前端项目部署方式主要分仨种:
- 与后端部署在一起,即部署不分离,由后端路由返回页面
- 前端单独部署,路由前端完成
- 奇美拉式部署,既有前端部署,又有后端返回页面,传说中的部署,最强无敌的融合,全栈工程师的梦幻天堂(bushi)
显然第二种方式前后端分离开更为灵活,更为合理,我们主要讨论第二种部署的方法
可以使用 git 来维护/部署,即你可以直接在服务器上git clone 你的前端工程,通过 npm i 安装依赖,在服务器上运行npm build 来打包,你甚至可以写个脚本(sh)自动执行这些过程,而当你的项目代码需要更新时,只需要把更改提交到仓库,再运行一遍脚本即可
但不推荐,安装依赖和编译都会浪费服务器的资源,推荐在开发环境(本地) build 好了以后再把静态文件传上服务器
还有更更灵活的思路,利用github action来安装依赖和打包,直接把静态文件发送到 nginx 服务器上,这样一来你只需要在仓库提交触发action, 便可以全自动部署好前端页面了(服务器连环境都不用安装 笑)
手动部署
nginx
安装完 nginx 后,我们发现用浏览器打开服务器 ip,就可以直接看到 nginx 的默认欢迎页面
| 欢迎 |
|---|
![]() |
该页面保存在:/var/www/html/index.nginx-debian.html
上传静态文件
ok,现在回到本地,把需要部署的前端静态文件传入/var/www/html/
scp -r ./react/codesheep root@127.0.0.1:/var/www/html/ # scp 的说明在后端部署部分里有
# 注意这里因为需要进入 var, 因此需要 root 权限,当然你也可以先传进非 root 用户的家目录,再 sudo 移过去
进入/etc/nginx/sites-available, 编辑 default 文件
cd /etc/nginx/sites-available
vim default
找到root /var/www/html这行,改成root/var/www/html/codesheep
修改完成,保存退出
输入:sudo nginx -t
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
出现ok,和successful就说明语法正确
重启 nginx 服务器
sudo service nginx restart
重新访问 ip,页面出现就成功了
nginx 配置
nginx 配置有很多,这里就列些常用最简单的
server {
listen 80 default_server; #监听端口
server_name _; #域名
root /var/www/html # 静态文件目录
location / {
try_files $uri $uri/ /index.html; # vue react 单页应用项目,配置路由到/index.html
}
location /api/ { # 注意斜杆不能少
proxy_pass http://localhost:8080/; # 跨域,后端地址,注意斜杆不能少
}
gzip on; # 页面 gzip 压缩
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 9;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/javascript application/json;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;
}
Codesheep 使用的配置:
user nginx;
worker_processes 1;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
#请按照以下协议配置
ssl_protocols TLSv1.2 TLSv1.3;
#请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 9;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/javascript application/json;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;
##
# loading balance
##
upstream backserver {
server xx.xx.xx.xx:8080 weight=7;
server xx.xx.xx.xx:8080 weight=3;
}
server {
listen 443 ssl;
charset utf-8;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
#请填写绑定证书的域名
server_name www.codesheep.xyz;
#请填写证书文件的相对路径或绝对路径
ssl_certificate cert/www.codesheep.xyz_bundle.crt;
#请填写私钥文件的相对路径或绝对路径
ssl_certificate_key cert/www.codesheep.xyz.key;
ssl_session_timeout 5m;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
root /usr/share/nginx/html/codesheep/;
#try_files $uri $uri/ =404;
try_files $uri $uri/ /index.html;
}
location /api/code-run {
proxy_pass http://backserver/code-run;
}
location /api/ {
proxy_pass http://xx.xx.xx.xx:8080/;
}
location /user/ {
proxy_pass http://xx.xx.xx.xx:8080/;
}
}
server {
listen 80;
listen [::]:80;
server_name www.codesheep.xyz;
# return 301 https://$host$request_uri;
return 301 https://$host;
}
}
自动部署
就像开头介绍的那样,使用github action来自动部署页面
docker
docker-compose
nignx 的配置
version: '3'
services:
nginx:
# 镜像名 如果需要指定版本 就把 latest 换成版本号
image: nginx:alpine
container_name: codesheep-web
restart: always
ports:
- 80:80
- 443:443
volumes:
# 证书映射
- ./cert:/etc/nginx/cert
# 配置文件映射
- ./nginx.conf:/etc/nginx/nginx.conf
# 页面目录
- ./html:/usr/share/nginx/html
# 主机本机时间文件映射 与本机时间同步
- /etc/localtime:/etc/localtime:ro
TIP
注意 volumes 的映射和上面 nginx 配置对应
