前置工作
-
服务器选取
这里我选择的是:CPU - 2核、内存 - 2GB、带宽:3Mbps,费用首年95元,公网IP为 43.143.200.250
-
了解模块端口
- 前端模块: 前台 8093 后台 8094
- 后端模块: 前台 7777 后台 8989
- mysql数据库端口3306 库名qz_blog 密码****
- redis端口6379 密码****
后端项目打包
- 在父目录下将原来SpringBoot版本修改为2.6.11
- 父模块pom文件下添加(framework下添加这段也可以)
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version> <!-- 使用你所需的版本 -->
</plugin>
<!-- 此插件必须放在父 POM 中 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<executions>
<!--
执行本插件的方法为,在主目录下执行如下命令:
mvn package assembly:single
对于 IntelliJ IDEA,生成的 JAR 包位于每个模块下的文件夹 target
-->
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<!-- 此处 IntelliJ IDEA 可能会报红,这是正常现象 -->
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifest>
<!-- 配置程序运行入口所在的类 此时选择自己对应的启动类-->
<!-- 自己的启动类path-->
<mainClass>com.qzhou.QzhouBlogApplication</mainClass>
</manifest>
<manifest>
<!-- 配置程序运行入口所在的类 此时选择自己对应的启动类 -->
<mainClass>com.qzhou.QzhouAdminApplication</mainClass>
</manifest>
</archive>
<!-- 设置 JAR 包输出目录 -->
<outputDirectory>${project.build.directory}/#maven-assembly-plugin</outputDirectory>
<!-- 设置打包后的 JAR 包的目录结构为默认 -->
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
- 子模块qzhou-blog 和qzhou-admin中添加
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<finalName>${project.artifactId}</finalName>
</build>
-
因为SpringBoot2.6.x 和Swagger2 存在兼容性问题,此时有两种选择:
4.1.注释掉Swagger,和他的启动注释(Shift+Ctrl+R),注意下面这个 }) 别漏了
4.2在使用了Swagger功能的模块yml文件中添加依赖
spring:
#解决SpringBoot2.6.x与swagger2的版本冲突问题
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER
- 解决循环依赖问题
spring:
#解决循环依赖
main:
allow-circular-references: true
-
测试
6.1.保证本地 redis已启动,本地msyql中有对应的数据库,连接的地址都是 localhost 下的服务
注:启动redis:直接在redis文件目录下点击redis-server.exe即可,若是redis 5.x版本或是点击服务启动后cmd窗口一闪而过,需要点击redis-cil.exe启动redis 客户端 输入命令 shutdown 后,redis服务端才可正常启动
6.2.此时在利用右边maven父模块中点击 clean 然后 install 打包,之后去本地的target目录下 cmd ,然后以下执行命令测试一下程序正确性
# 在对应的文件下执行下面命令能运行Java程序
java -jar qzhou-blog.jar
java -jar qzhou-admin.jar
- 以上测试程序正确的话,将 localhost 全部替换为自己的服务器公网IP(我的是43.143.200.250),然后在设置一下服务器上的MySQL 和Redis 密码,然后在打包成 jar 文件 (打包完成后我的前台后后台的jar包大小相等)
注:
- Idea 全局替换的快捷键是 Shift+Ctrl+R ;
- 此时可以先设置密码然后再去修改服务器上配置文件,注意密码保持一样即可
前端项目打包
-
把sg-blog-vue(前端前台)项目的config目录下的index.js文件,port属性值设置为8093
-
把sg-vue-admin(前端后台)项目下的vue.config.js文件的port值设置为8094
-
修改四个文件下的 VUE_APP_BASE_API 为自己对应的服务器IP+与后端对接的端口 8989
-
把sg-vue-admin\src\router\index.js文件修改为如下
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '@/layout'
/**
* Note: sub-menu only appear when route children.length >= 1
* Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
*
* hidden: true if set true, item will not show in the sidebar(default is false)
* alwaysShow: true if set true, will always show the root menu
* if not set alwaysShow, when item has more than one children route,
* it will becomes nested mode, otherwise not show the root menu
* redirect: noRedirect if set noRedirect will no redirect in the breadcrumb
* name:'router-name' the name is used by <keep-alive> (must set!!!)
* meta : {
roles: ['admin','editor'] control the page roles (you can set multiple roles)
title: 'title' the name show in sidebar and breadcrumb (recommend set)
icon: 'svg-name'/'el-icon-x' the icon show in the sidebar
breadcrumb: false if set false, the item will hidden in breadcrumb(default is true)
activeMenu: '/example/list' if set path, the sidebar will highlight the path you set
}
*/
/**
* constantRoutes
* a base page that does not have permission requirements
* all roles can be accessed
*/
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: '首页', icon: 'dashboard' }
}]
},
{
path: '/write',
component: Layout,
children: [{
path: '/',
name: 'Write',
component: () => import('@/views/content/article/write/index'),
hidden: true
}]
},
{
path: '/system/user',
component: Layout,
children: [{
path: '/',
name: 'User',
component: () => import('@/views/system/user'),
hidden: true
}]
},
{
path: '/system/role',
component: Layout,
children: [{
path: '/',
name: 'role',
component: () => import('@/views/system/role'),
hidden: true
}]
},
{
path: '/system/menu',
component: Layout,
children: [{
path: '/',
name: 'menu',
component: () => import('@/views/system/menu'),
hidden: true
}]
},
{
path: '/system/role',
component: Layout,
children: [{
path: '/',
name: 'role',
component: () => import('@/views/system/role'),
hidden: true
}]
},
{
path: '/content/article',
component: Layout,
children: [{
path: '/',
name: 'article',
component: () => import('@/views/content/article/index'),
hidden: true
}]
},
{
path: '/content/category',
component: Layout,
children: [{
path: '/',
name: 'category',
component: () => import('@/views/content/category/index'),
hidden: true
}]
},
{
path: '/content/link',
component: Layout,
children: [{
path: '/',
name: 'link',
component: () => import('@/views/content/link/index'),
hidden: true
}]
},
{
path: '/content/tag',
component: Layout,
children: [{
path: '/',
name: 'tag',
component: () => import('@/views/content/tag/index'),
hidden: true
}]
}
]
const createRouter = () => new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})
const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
export default router
- 把sg-vue-admin\src\main.js文件,注释掉下面的内容
-
全局搜索(可用Idea打开)将剩下的localhost 替换为自己的IP(43.143.200.250)
-
打开控制台执行下面命令即可生成dist 文件
前端前台 qzhou-blog
npm run build
前端后台 qzhou-admin
npm run build:prod
服务器下
-
开放防火墙
firewall-cmd --zone=public --add-port=80/tcp --permanent firewall-cmd --zone=public --add-port=7777/tcp --permanent firewall-cmd --zone=public --add-port=8989/tcp --permanent firewall-cmd --zone=public --add-port=6379/tcp --permanent firewall-cmd --zone=public --add-port=3306/tcp --permanent firewall-cmd --zone=public --add-port=8093/tcp --permanent firewall-cmd --zone=public --add-port=8094/tcp --permanent
#重启防火墙 systemctl restart firewalld.service #查看端口是否开放命令 firewall-cmd --zone=public --query-port=8093/tcp
-
配置安全组规则,开放安全组(腾讯云为例)
Docker容器中
- docker 安装和配置和阿里云镜像加速
黑马2023飞书文档:
https://b11et3un53m.feishu.cn/wiki/Rfocw7ctXij2RBkShcucLZbrn2d?chunked=false
- 拉取所需镜像
docker pull java:8
docker pull mysql:8.0.19
docker pull redis:6.0.8
docker pull nginx:1.18.0
-
编写Dockerfile文件,把 jar 包构建成镜像
3.1.首先创建目录存放jar 包 ,如在root下创建 blog ,把后端生成的jar 包传进来
3.2.编写Dockerfile文件,这里我们两个jar包分别打成两个镜像,因为一个目录下只能有一个Dockerfile文件,所以当构建完第一个镜像之后修改对应的Dockerfile文件
#第一个镜像
#基础镜像使用jdk1.8
FROM java:8
# VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名
ADD qzhou-blog.jar qzhou-blog.jar
# 运行jar包
RUN bash -c 'touch /qzhou-blog.jar'
ENTRYPOINT ["java","-jar","/qzhou-blog.jar"]
#暴露7777端口作为微服务
EXPOSE 7777
# #第二个镜像
# #基础镜像使用jdk1.8
# FROM java:8
# # VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
# VOLUME /tmp
# # 将jar包添加到容器中并更名
# ADD qzhou-admin.jar qzhou-admin.jar
# # 运行jar包
# RUN bash -c 'touch /qzhou-admin.jar'
# ENTRYPOINT ["java","-jar","/qzhou-admin.jar"]
# #暴露8989端口作为微服务
# EXPOSE 8989
-
构建镜像
docker build -t qzhou-blog:1.0 . 命令说明: - docker build : 就是构建一个docker镜像 - -t qzhou-blog:1.0 :-t参数是指定镜像的名称(repository和tag) - . : 最后的点是指构建时Dockerfile所在路径,由于我们进入了demo目录,所以指定的是.代表当前目录,也可以直接指定Dockerfile目录:
# 此时用不到但是可能用到的命令 # 直接指定Dockerfile目录命令 docker build -t docker-demo:1.0 /root/demo #删除指定镜像 加 -f 表示强制删除 docker rmi -f qzhou-blog:1.0
4.1.检查一下,此时应该生成了jar 包对应的两个镜像文件
# 查看镜像列表: docker images
-
使用Docker-compose容器编排,创建容器
5.1.编写docker-compose.yml文件,使用docker-compose容器编排管理容器运行
# 最终版 Docker Compose 文件
version: "3"
services:
qzhou-blog:
image: qzhou-blog:1.0
container_name: qzhou-blog
ports:
- "7777:7777"
volumes:
- /root/blog/qzhou-blog:/data/qzhou-blog
networks:
- blog_network
depends_on:
- redis
- mysql
- nginx
qzhou-admin:
image: qzhou-admin:1.0
container_name: qzhou-admin
ports:
- "8989:8989"
volumes:
- /root/blog/qzhou-admin:/data/qzhou-admin
networks:
- blog_network
depends_on:
- redis
- mysql
- nginx
redis:
image: redis:6.0.8
container_name: redis
ports:
- "6379:6379"
volumes:
- /root/blog/redis/redis.conf:/etc/redis/redis.conf
- /root/blog/redis/data:/data
networks:
- blog_network
command: redis-server /etc/redis/redis.conf
mysql:
image: mysql:8.0.19
container_name: mysql
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: zhouq
ports:
- "3306:3306"
volumes:
- /root/blog/mysql/db:/var/lib/mysql
- /root/blog/mysql/conf/my.cnf:/etc/my.cnf
- /root/blog/mysql/init:/docker-entrypoint-initdb.d
networks:
- blog_network
command: --default-authentication-plugin=mysql_native_password
nginx:
image: nginx:1.18.0
container_name: nginx
ports:
- "80:80"
- "8093:8093"
- "8094:8094"
volumes:
- /root/blog/nginx/html:/usr/share/nginx/html
- /root/blog/nginx/logs:/var/log/nginx
- /root/blog/nginx/conf:/etc/nginx
networks:
- blog_network
#创建自定义网络
networks:
blog_network:
5.2.检查当前目录下compose.yml文件是否有语法错误 ; 可以使用在线yml文档检验器查询语法是否正确(如 https://www.bejson.com/validators/yaml_editor/)
docker compose config -q
5.3.启动所有docker-compose服务并后台运行
# -d 后台运行
docker compose up -d
# 此时用不到但是可能用到的命令
#删除指定容器命令
docker rm nginx
#停止并删除现有的冲突容器:
docker stop nginx && docker rm nginx
查看容器信息
docker ps -a
-
配置nginx
6.1.首先创建一个nginx容器,只是为了复制出配置
# 1.运行容器
docker run -p 80:80 --name nginx -d nginx:1.18.0
# 2.将容器内的配置文件拷贝到当前目录/blog中:
docker container cp nginx:/etc/nginx .
# 3.将文件nginx修改为conf
mv nginx conf
# 4.创建文件夹nginx
mkdir nginx
# 5.将conf目录拷贝到nginx目录
cp -r conf nginx/
# 6.删除conf目录
rm -rf conf
# 3.停止并删除容器
docker stop nginx && docker rm nginx
-
配置MYSQL
7.1.使用远程连接软件(如Navicat)连接容器内数据库
7.2.创建数据qz_blog(与后端程序yml文件中配置的保持一致)
7.3.导入Sql 文件
-
配置Redis
8.1.获取redis对应版本的配置文件 : 因为docker拉取的redis镜像是没有redis.conf文件的。因此,就需要我们官网上找一个相对应版本的的redis.conf配置文件。redis配置文件官网 https://redis.io/docs/manual/config/
8.2.进入/root/blog/redis目录下创建一个redis.conf文件,然后将redis配置文件内容粘贴进去
修改redis密码:
修改bind为0.0.0.0(任何机器都能够访问)
为了避免和docker中的-d参数冲突,将后台启动设置为no(daemonize no)
关闭保护模式(protected-mode no)
-
测试后端端口
9.1.重启现在未启动的容器实例,STATUS !=exited 就行,下面这是启动了
#这里默认是容器ID 如果上面docker-compose.yml 配置了 container_name: nginx 那就是 docker restart nginx
9.2.查看程序运行日志
#查看指定容器qzhou-admin运行日志 加 -f 参数就是持续查询
docker logs -f qzhou-admin
- 前端dist文件放入容器内的nginx
10.1.前端项目放入本地nginx测试
10.2.将打包好后的两个dist文件夹重命名发送到 /root/blog/nginx/html 目录下
10.3.修改conf目录下的nginx.conf配置文件 ,在http{} 下添加
server {
listen 8093;
server_name localhost;
location / {
root /usr/share/nginx/html/blog-dist;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
server {
listen 8094;
server_name localhost;
location / {
root /usr/share/nginx/html/admin-dist;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
10.4.重启nginx
docker restart nginx
- 成功截图
后言
(后端)程序代码错误或后续升级重新部署流程:
-
停止并删除已有容器
# 如qzhou-blog docker stop qzhou-blog && docker rm qzhou-blog # 查看容器 docker ps -a
-
删除错误镜像
docker rmi -f qzhou-blog
-
本地生成 jar 包并传入服务器指定目录下(与DockerFile一级目录)
-
生成新的镜像
docker build -t qzhou-blog:1.0 .
-
构建容器
docker compose up -d