前言
作为一名程序员,想必每个人都想拥有一个自己的网站,奈何技术实现简单,但是部署却成了最大的问题。作为前端开发的我也早就在没毕业就有了开发一个属于我的网站,然后部署上线了,可惜当时接触的知识有限,不足支撑我完成这套流程,如今决定来实现这个目标,进行简单的网站部署,本篇文章记录下我自己的项目部署上线的过程,也是我第一次独立部署整个项目,末尾也会记录部署时所踩的坑。
服务器选择及连接
部署项目最重要的前提是服务器了,服务器我选择的是某里云的云服务器,按照官方的指示配置好服务器的配置后,就能在指定的网页进行远程连接了。但是据我体验在网页连接服务器还是有点卡顿的,所以这里通过一些客户端工具与连接到服务器了,本篇的服务器连接工具是MobaXterm Xserver with SSH, telnet, RDP, VNC and X11 - Download,进入地址,选择社区版下载即可。
下载完成后双击打开,点击右上角的会话->新建会话->选择SSH,输入你的远程主机ip,指定用户未root,到这里点击Ok即可(其它配置可以根据自己的需求配置)
第一次连接会让我们输入密码,我们按照指示输入密码后保存即可进入到终端界面。
安装docker
可能有很多前端朋友没有接触过docker,如果是这种情况,又有部署需求,可以转到Docker 教程 | 菜鸟教程进行学习,只需要知道常用的命令即可,下面是docker的介绍:
Docker 是一个用于开发,交付和运行应用程序的开放平台。Docker 使您能够将应用程序与基础架构分开,从而可以快速交付软件。借助 Docker,您可以与管理应用程序相同的方式来管理基础架构。通过利用 Docker 的方法来快速交付,测试和部署代码,您可以大大减少编写代码和在生产环境中运行代码之间的延迟。
在安装docker之前,我们需要先安装yum的工具包(yum可以简单理解为npm,它是用来帮助我们下载和管理包的)
# 1. 安装工具包
yum install -y yum-utils device-mapper-persistent-data lvm2
# 2. 设置镜像源
yum-config-manager --add-repo https://mirrors.aliyun.com/dockerce/linux/centos/docker-ce.repo
之后就是安装docker了
# 1. 安装docker
yum install -y docker-ce
# 2. 等待下载完成后,启动docker
systemctl start docker
# 3. 查看docker是否启动
systemctl status docker
systemctl status docker如果看到下图绿色的active,就证明我们的docker安装并启动成功了
配置镜像加速
在后续操作开始之前,我们还需要配置下docker镜像,可以理解为npm镜像,为了让我们后续拉取镜像时有个加速效果,至少不至于拉取失败。
# 创建配置文件
mkdir -p /etc/docker
# 添加镜像地址
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker.1panelproxy.com",
"https://2m11665s.mirror.aliyuncs.com",
"https://registry.docker-cn.com",
"https://dockerhub.azk8s.cn",
"https://docker.mirrors.ustc.edu.cn",
"http://hub-mirror.c.163.com",
"https://k8s.gcr.io",
"https://github-releases.githubusercontent.com",
"https://vsxcs7sq.mirror.aliyuncs.com",
"https://ustc-edu-cn.mirror.aliyuncs.com"]
}
EOF
# 最后别忘了重启docker使这些配置生效
systemctl restart docker
配置docker命令别名(可选)
正常使用是使用docker ps ,docker images等,命名不但长,而且列出的菜单有的也不是我们想要的,这里我们简化命令。
# 打开文件
vim ~/.bashrc
# 打开后在最末尾加入下面两行
alias dps='docker ps -a --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"'
alias dis='docker images'
# 最后使用source ~/.bashrc让配置文件生效
source ~/.bashrc
# 之后我们就能使用dps查看容器,dis查看镜像了
前端ui部署
前端项目的托管我们需要借助nginx,所以我们要做的就是先部署nginx,下面列出部署过程及我的nginx配置并做出解释。
1.拉取镜像docker pull nginx
2. 创建挂载目录
mkdir -p /home/nginx/conf
mkdir -p /home/nginx/log
mkdir -p /home/nginx/html
3.创建文件
# 创建nginx配置文件
cd /home/nginx/conf
touch nginx.conf
# 创建日志文件
cd /home/nginx/log
touch access.log
touch error.log
4. 编写nginx配置文件
由于我这边的前端项目包括客户端和管理端,这里创建两个server块分别监听两个端口。
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
server {
listen 8888;
server_name localhost;
location / {
root /usr/share/nginx/html/mblog;
index index.html index.htm;
}
location /api {
# 反向代理地址写你后端的地址
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
server {
listen 9999;
server_name localhost;
location / {
root /usr/share/nginx/html/mblog-management;
index index.html index.htm;
}
location /api {
# 反向代理地址写你后端的地址
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
}
6. 运行容器
# 1.这里-p映射的端口确保和容器内一致 (8888:8888 前面的8888是访问宿主机的端口,后面的8888是nginx容器监听的端口)
# 2.结合nginx配置文件和你的端口,仔细检查-v你的挂载目录是否正确!!!
docker run -p 8888:8888 -p 9999:9999 --name mblog-nginx -v /home/nginx/conf/nginx.conf:/etc/nginx/nginx.conf -v /home/nginx/log:/var/log/nginx -v /home/nginx/ui:/usr/share/nginx/html -d nginx:latest
运行后会出现一个容器id,我们通过绿框中的命令查看这个容器的日志,如果没有报错,我们的nginx就启动成功了。
7.打包前端代码
博主的前端构建工具用的是vite,只需要进入前端项目目录npm run build即可,最后将打包后的代码直接拖入到你服务器指定的html目录即可。
到这里,按道理来讲,我们就能通过服务器的地址访问到我们的html了,但是到这一步就踩了三个坑,踩坑的解决方法放到最后总结,如果你遇到了相同的问题可以到文末尾查看。
坑1 无法访问路径对应的html
坑2 history路由模式,页面刷新后url后被自动加上/导致页面访问资源失败,报错403
坑3 访问后端接口报502网关异常
mysql部署
1.拉取镜像docker pull mysql:5.5,:后面指定你自己的mysql版本,我这里的是5.5
2. 创建挂载目录
mkdir -p /home/mysql/log
mkdir -p /home/mysql/data
mkdir -p /home/mysql/conf
3.运行容器
# 下面的命令,修改密码为你的mysql连接密码即可
docker run \
--name mysql \
-d \
-p 3306:3306 \
--restart unless-stopped \
-v /mydata/mysql/log:/var/log/mysql \
-v /mydata/mysql/data:/var/lib/mysql \
-v /mydata/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=你的MYSQL连接密码 \
mysql:5.5
4. 测试数据库连接
这里使用navicat进行数据库连接,输入主机及你刚刚启动mysql容器的密码点击测试连接,提示连接成功说明我们的mysql就部署成功并且能够连接了。
minio部署
1.拉取镜像docker pull minio/minio
2. 创建挂载目录
mkdir -p /home/minio/config
mkdir -p /home/minio/data
3.运行容器
# 下面的命令修改为你的MINIO_ACCESS_KEY和MINIO_SECRET_KEY
# 注意:账号长度必须大于等于5,密码长度必须大于等于8位
docker run -p 9000:9000 -p 9090:9090 \
--net=host \
--name minio \
-d --restart=always \
-e "MINIO_ACCESS_KEY=你的minio账号" \
-e "MINIO_SECRET_KEY=你的minio密码" \
-v /home/minio/data:/data \
-v /home/minio/config:/root/.minio \
minio/minio server \
/data --console-address ":9090" -address ":9000"
4.测试是否部署成功
直接访问你的服务器ip+9090端口,如果能看到这个页面说明minio部署成功了。
后端jar包部署
1. 新建目录 mkdir -p /home/jar
2. 编写dockerfile
FROM openjdk:8-jre
# 挂载目录
VOLUME /home/jar
# 创建目录
RUN mkdir -p /home/jar
# 指定路径
WORKDIR /home/jar
# 复制jar文件到路径
COPY ./serve.jar /home/jar/serve.jar
# 启动系统服务
ENTRYPOINT ["java","-jar","serve.jar"]
3.拷贝jar包
这里博主后端的技术栈是spring boot 2,maven打包后直接拖到指定的目录下即可。
4. 构建镜像
# 注意在dockerfile同级目录下运行
docker build -t serve .
5.启动容器
docker run -p 8080:8080 --name serve -d serve
坑4 服务启动失败:提示no main manifest attribute, in XXX.jar
踩坑及解决方案
坑1 无法访问路径对应的html
这里找了很久,也百度了很多,最开始以为是nginx配置写错了,有转到以为防火墙的问题,关闭防火墙也没解决,最终原因是因为某里云的服务器需要开放端口访问,操作方法如下:
进入服务器的控制台->安全组->点击你的服务器实例->点击手动添加
在目的栏填写你开放的端口即可。
坑2 页面刷新后url后被自动加上/,报错403
描述:进入首页正常,进行路由跳转后,刷新页面url后被自动加上了个/,导致访问页面资源失败,页面报403,这时去查看nginx的error.log日志发现了如下报错:
这个错误的意思是因为我们访问了一个没有权限访问的路径,但是我们的路由名称确实是/video,为什么会强制加个/呢?
原因是我们的bublic下有个文件夹也叫video,也就是我们打包后的dist目录下也有个文件夹叫video
当刷新时我们的url被重定向到了video/,解决方法就是避免路由地址的名称和静态资源的名称重复,将/video的路由修改为/videos即可。
坑3 访问后端接口报502网关异常
前端调用后端接口返回502,这是查看nginx的错误日志会发现这么一个日志:
cilent后面的ip是用户的ip,server就是我们的服务器,这个报错的意思就是在说,用户在请求/api/link这个接口的时候上游服务器(也就是我们的后端服务)没有响应。调查了一下出现这个报错的原因有很多,可以按照下面步骤排查:
-
netstat -ntlp | grep 8080查看服务的端口是否还在 -
查看后端服务的容器日志是否有报错,或者服务是否还在
-
直接请求后端服务看能否收到响应,我这里可以请求成功
如果到这里都没问题,那就说明不是我们后端服务的问题了,这是后就需要来检查我们的nginx配置是否有问题。
我们这里nginx可以正常启动,说明没有语法错误,并且访问监听端口的根路径也能正确的返回首页,那么问题就只能出现在反向代理这块儿的配置了。
location /api {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
主要看proxy_pass这个地址,造成502的原因可能就是这里代理过去的请求没有调通,这里我们直接在nginx容器的外部请求这个地址
发现是可以正常响应的,那么我们再进入nginx容器内部去请求呢?
发现请求失败了,所以猜测可能是容器内请求和容器外请求的方式不太一样,最终解决方案是将代理的地址直接改成服务器的ip地址:
location /api {
# 假设我服务器的ip是103.8.11.111,这里改成你的服务器ip
proxy_pass http://103.8.11.111:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
然后重新启动容器,问题解决。
坑4 服务启动失败:提示no main manifest attribute, in XXX.jar
当我们运行jar包时会抛出这个错误,可以理解为找不到运行的入口方法,解决方法是在pom.xml的plugins标签中加入maven打包的插件
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.6.13</version>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>