部署篇

这里是最最最激动人心的地方,终于要部署你千辛万苦写完的项目啦

这里部署的顺序,可以反着来,因为前端的部署一定情况下依赖于后端的部署,因此我们可以暂且先放眼于后端,因为操作的指令都是一样的,细节的区别再分别详细介绍

后端部署

数据库建表,服务端容器配置,这些内容暂时不属于笔记分享范围

后端程序需要打包或编译,以 java 为例

java 打包部署,因为本人技术有限推荐使用 jar 打包,减少部署的麻烦,如果你并不想把所有东西都打成 jar 包,可以去搜索学习一下依赖分离,和容器分离地打包方式,我仅使用fat jar 演示

手动部署

手动部署就是人工把服务端上传到服务器运行部署,虽然麻烦,且一旦出现问题每次都要重新打包上传,但是通过这个方法你可以熟练地掌握这些细节

服务端打包

虽然使用 jar 部署,但还是推荐把配置文件分离,这样可以直接在服务上修改配置后重新部署,减少手动上传的次数

TIP

关于为什么需要把配置文件分离?以上的理由算一个,还有一个理由:开发环境和部署环境是不一样的,有可能你需要把配置文件里的名称,或路径更改,那么分离配置文件可以灵活地做到及时修改

进入 idea

  1. 用 maven 打包,在 lifecycle 中选中cleanjar 运行 image
  2. 打包完成,在target目录下,存放着我们打好的 jar 包了 image

TIP

其实你的配置文件已经被默认复制进了 jar 包里,但是没必要花精力去阻止它们被复制,我们只需要在服务器运行时,使用新的配置(application.ymlapplication.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 和配置文件上传到服务器上image

好了,接下来直接到服务器上查看 image

上传完成

配置服务端

登录服务器后,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/

image

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

image

没有问题!

持续运行服务端

读到上面,你已经兴高采烈地启动后端了,可是又出现问题了,你只要断开 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 博客 open in new window

半自动部署

可以使用 idea 部署,这个随便看个教程就知道了,其实无非也是配置好了 sftp,能更快捷地帮你传输文件而已,你需要更精细地活,还是要亲自操作

前端部署

web 前端项目部署方式主要分仨种:

  1. 与后端部署在一起,即部署不分离,由后端路由返回页面
  2. 前端单独部署,路由前端完成
  3. 奇美拉式部署,既有前端部署,又有后端返回页面,传说中的部署,最强无敌的融合,全栈工程师的梦幻天堂(bushi)

显然第二种方式前后端分离开更为灵活,更为合理,我们主要讨论第二种部署的方法

可以使用 git 来维护/部署,即你可以直接在服务器上git clone 你的前端工程,通过 npm i 安装依赖,在服务器上运行npm build 来打包,你甚至可以写个脚本(sh)自动执行这些过程,而当你的项目代码需要更新时,只需要把更改提交到仓库,再运行一遍脚本即可

但不推荐,安装依赖和编译都会浪费服务器的资源,推荐在开发环境(本地) build 好了以后再把静态文件传上服务器

还有更更灵活的思路,利用github action来安装依赖和打包,直接把静态文件发送到 nginx 服务器上,这样一来你只需要在仓库提交触发action, 便可以全自动部署好前端页面了(服务器连环境都不用安装 笑)

手动部署

nginx

安装完 nginx 后,我们发现用浏览器打开服务器 ip,就可以直接看到 nginx 的默认欢迎页面

欢迎
image

该页面保存在:/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 配置对应