1.前言
先一句话总结一下,前后端调通的关键是IP地址。
最终项目环境结构:本地部署若依后端服务器,docker部署mysql、redis、node或nginx容器。通过node容器或nginx部署若依前端,并调用部署在本地的后端服务器。
声明:本文所涉及的问题点仅针对本文所处时间以及发布文章时所用的各软件版本,不一定适用于任何时间和任何版本。
- 时间点:2023年11月
- macOS版本:13.2.1
- docker版本:24.0.6
- idea版本:2022.2.3 (Ultimate Edition)
- 若依版本:3.8.6(前后端分离版)
2.若依项目初步搭建和简单总结
2.1官方资料:
http://doc.ruoyi.vip/ruoyi-vue/
2.2系统需求
- JDK >= 1.8
- MySQL >= 5.7
- Maven >= 3.0
- Node >= 12
- Redis >= 3
2.3本地初步搭建
按照若依官方资料进行搭建即可,相关资料多如繁星不再赘述。
2.4简单总结
2.4.1后端相关要点
- JDK版本就选1.8
- 我的maven3.0是idea自带的,没有额外安装
- mysql默认3306接口,redis默认6379接口
- 编辑项目配置文件 application-druid.yml 中的数据库相关信息
- 编辑项目配置文件 application.yml 中的文件路径
- 编辑日志配置文件 logback.xml 中的文件路径
2.4.2前端相关要点
- 使用node为项目安装依赖时,直接使用另一个源
npm install --registry=https://registry.npmmirror.***
- 由于我下载的node版本较高,加载若依项目时会报错,相关资料可自行查询,此处直接放解决方法,在 package.json 里的对应位置添加下列语句
export NODE_OPTIONS=--openssl-legacy-provider &&
2.5(随笔)决定搞点事做
搭建基本的若依项目是相对比较简单的,各类资料非常齐全,项目也顺利跑起来了。
但是,看着mysql占用了我系统设置里面的一个菜单,看着redis、node占用着两个终端在那挂着,想着以后还要去关闭开启这些服务,内心萌生了一丝不爽。
于是我产生了一个想法,把这些东西都用docker部署,全部容器化之后,是不是就清爽很多了。
于是,开始本文的重头戏。
3.基于docker的容器化部署
3.1准备docker
直接从官网下载docker desktop,mac下载时需要注意芯片对应的版本(intel版和arm架构版)
关于获取镜像,有些帖子里面提到了去hub里面获取,但是本文发文的时期而言,hub的网页已经封锁了,国内无法访问,但是不影响用指令拉取镜像。
3.2部署mysql
3.2.1部署mysql容器
(1)拉取镜像
不输入版本号就默认拉取最新版
docker pull mysql
(2)准备数据卷
默认数据库的数据是放在容器里面的,这样的话当容器删除会导致数据丢失,如果要实现 当删除容器的时候不删除容器里面的mysql数据 ,这个时候启动容器的时候就可以把 mysql 数据挂载到外部
创建两个目录用于保存mysql的数据和配置
mkdir -p /Users/dyrain/Downloads/Docker/mysql/conf
mkdir -p /Users/dyrain/Downloads/Docker/mysql/lib
(3)创建mysql容器
因为之前的本地项目就是使用的3306端口访问mysql,并且mysql容器内也是默认3306端口提供服务,所以用本地3306端口映射容器的3306端口就可以了,后端项目不用进行别的配置变更。
docker run -p 3306:3306 --name mysql -v /Users/dyrain/Downloads/Docker/mysql/conf:/etc/mysql/conf.d -v /Users/dyrain/Downloads/Docker/mysql/lib:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql
- -p 指定端口映射
- -v 指定数据挂载目录
- -e MYSQL_ROOT_PASSWORD设置数据库密码
- -d 进程后台执行
3.2.2准备数据库
(1)进入mysql容器
加上sudo之后,是使用管理员权限执行,需要输入电脑密码
sudo docker exec -it mysql /bin/bash
(2)登陆数据库
mysql -u root -p
输入数据库密码之后就进入mysql了,之后的操作与搭建本地若依环境一致,进行创建数据库、建表、导入数据等操作。
由于挂载了本地地址作为数据库保存地址,在导入了数据之后可以简单查看一下
至此,mysql准备完毕
3.3部署redis
由于目前项目并不需要用到redis的数据持久化,所以部署redis非常简单,并且后端不需要更改配置。如果需要做持久化,可自行查看相关资料。
(1)拉取镜像
此处拉取redis指定版本号7.0.14(进行下练习而已,不带版本号也可以,没有影响)
docker pull redis:7.0.14
(2)创建redis容器
指定默认端口6379进行映射
docker run --name ruoyi-redis -p 6379:6379 -d redis:7.0.14
(3)进入容器
docker exec -it ruoyi-redis /bin/bash
(4)启动并进入redis
-h 指定要连接的redis节点的IP地址,默认是127.0.0.1
redis-cli -h 127.0.0.1
进入redis后可以简单进行测试
至此,redis准备完毕
3.3部署node
从这里开始就是踩坑居多的环节了,赶时间的话,看见标题带有失败标记的,可以跳过不看。
3.3.1拉取镜像
害怕出现node版本过高导致无法加载项目的,可以选择拉取较低版本。由于我已经修改过配置了,所以依然拉取最新版
docker pull node
3.3.2挂载数据卷并创建容器
指定端口3000进行映射,同时挂载本地前端项目文件夹ruoyi-ui到容器内的目录/app/ruoyi_node
docker run -it -d --name ruoyi-node -p 3000:3000 -v /Users/dyrain/Downloads/RuoYi-Vue-master/ruoyi-ui:/app/ruoyi_node node /bin/bash
3.3.3调整前端项目端口
为了方便容器端口对应,将本地目录ruoyi-ui下的 vue.config.js 打开,修改端口为3000
3.3.4进入容器并加载前端项目
(1)进入容器
docker exec -it ruoyi-node /bin/bash
(2)进入目录
进入到挂载了前端项目的目录下/app/ruoyi_node
cd app/ruoyi_node
(3)安装依赖
npm install --registry=https://registry.npmmirror.***
(4)运行项目
插播一条:先保证后端服务已启动起来了
执行以下指令启动项目
npm run dev
3.3.5运行结果
执行指令后终端显示如下信息
相较于本地启动,容器内启动并不会自动弹出前端主页
通过映射的本地3000端口访问前端项目(后端服务已启动)
docker内node容器访问本地后端服务器报错(系统接口500异常)
至此,node部署完毕,进入排错阶段
3.4项目排错
一些分析的过程我就不写了,重点提一些网上能搜到的,但是并不能解决问题的方法
3.4.1报错原因
本地目录ruoyi-ui下的 vue.config.js 中关于地址的配置有问题,此处不能用localhost
3.4.2(失败)使用localhost对应的IP地址
查看localhost对应的IP地址
nslookup localhost
得到localhost对应的IP地址127.0.0.1
进入容器测试能否ping通这个地址
如果报错找不到ping命令,执行下面两条指令安装一下即可
apt-get update
执行安装命令
apt-get install iputils-ping
安装完毕后再次尝试ping一下,通的
修改本地目录ruoyi-ui下的 vue.config.js 中关于地址的配置
重新通过node容器进入/app/ruoyi_node目录加载前端项目
npm run dev
测试结果:依然系统接口500异常
3.4.3(失败)通过bridge上的IP地址
通过分析和查阅资料,了解到当前docker是通过bridge的网络策略来连接的。
查询网桥上的地址
docker inspect bridge
找到本机在网桥上面的地址为172.17.0.1
并且网桥上还能看见容器的ip
感觉这个ip可能稳了,从容器内ping一下,通的
修改本地目录ruoyi-ui下的 vue.config.js 中关于地址的配置
跑起来试试,依然失败:系统接口500异常
3.4.4(成功)通过host.docker.internal别名
说实话,这个方法在我调试的过程中并没有成功,是我在准备这篇文章的时候成功的,神奇😓。。。。并且网上也有别的博主发文说这个方法没有成功,因此我更推荐使用下一个方法。
根据容器访问宿主机的关键字进行搜索,找到了让容器通过host.docker.internal作为主机的别名来进行访问,此别名是内置的
ping一下看看
使用curl指令调后端服务器生成验证码的接口试试看
curl http://host.docker.internal:8080/captchaImage
返回结果如下:
很好,感觉有戏,更改配置,跑一把
成功登陆
3.4.5(成功)通过ifconfig查找宿主机的IP地址
通过host.docker.internal的方法,我在调试过程遇见过失败的情况,而使用本地IP地址的方法一次成功,所以我更建议使用这个方法。
终端输入指令
ifconfig
其中en0对应的就是本机的IP地址
将这个IP地址写到配置文件并启动项目,启动成功
3.5部署nginx
对于前端的部署,其实还有一个方法就是通过node打包前端项目,并发布到nginx容器里来实现
3.5.1拉取镜像
docker pull nginx
3.5.2创建nginx容器
这个创建过程基本梳理为:创建nginx容器1 -> 本地新建数据卷文件夹 -> 拷贝配置文件到挂载用的本地文件夹 -> 删除nginx容器1 -> 挂载本地数据卷并新建nginx容器2
(1)创建临时nginx容器1
如果端口有冲突,需要进行调整
docker run --name myNginx -p 8090:80 -d nginx
(2)准备挂载文件夹
html文件夹下面将用于放置打包好的前端项目
mkdir -p /Users/dyrain/Downloads/Docker/nginx/logs
mkdir -p /Users/dyrain/Downloads/Docker/nginx/html
mkdir -p /Users/dyrain/Downloads/Docker/nginx/conf
mkdir -p /Users/dyrain/Downloads/Docker/nginx/conf.d
(3)拷贝配置文件
docker cp myNginx:/etc/nginx/nginx.conf /Users/dyrain/Downloads/Docker/nginx/conf/nginx.conf
docker cp myNginx:/etc/nginx/conf.d/default.conf /Users/dyrain/Downloads/Docker/nginx/conf.d/default.conf
docker cp myNginx:/usr/share/nginx/html /Users/dyrain/Downloads/Docker/nginx/
(4)停止并删除nginx容器1
docker stop myNginx
docker rm myNginx
(5)创建nginx容器2
docker run --name myNginx -p 8090:80 -v /Users/dyrain/Downloads/Docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf -v /Users/dyrain/Downloads/Docker/nginx/conf.d:/etc/nginx/conf.d -v /Users/dyrain/Downloads/Docker/nginx/logs:/var/log/nginx -v /Users/dyrain/Downloads/Docker/nginx/html:/usr/share/nginx/html -d nginx
3.5.3打包前端项目
在本地目录ruoyi-ui下先确认 vue.config.js 中关于地址的配置,这个地址需要与nginx配置文件中的地址一致
通过node容器在/app/ruoyi_node目录下执行以下命令即可进行打包
npm run build:prod
打包后得到一个文件夹dist
将该文件夹拷贝到nginx容器挂载的目录下
3.5.4编写nginx配置文件
一共有两个配置文件 nginx.conf 和 default.conf
nginx.conf 配置如下
user root;
worker_processes 1;
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"';
a***ess_log /var/log/nginx/a***ess.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
其中最后一行语句包含了对 default.conf 的引用,所以将项目相关的配置写到 default.conf 里就可以了。
server {
listen 80;
listen [::]:80;
server_name localhost;
#a***ess_log /var/log/nginx/host.a***ess.log main;
location / {
root /usr/share/nginx/html/dist;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
location /prod-api/{
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://host.docker.internal:8080/;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny a***ess to .hta***ess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
/usr/share/nginx/html/dist为打包后的前端项目存放的路径
host.docker.internal:8080为后端服务地址和端口
3.5.5启动nginx服务
可以使用以下指令重新加载配置文件,或者重启nginx容器
nginx -s reload
3.5.6结果
3.5.7(拓展)用本机IP地址也可以
步骤3.5.3和3.5.4中的地址,可以使用之前node上验证成功的本地IP地址来代替host.docker.internal
3.6容器展示
node容器的话,每次启动项目还要多一些步骤:进入容器,进入项目目录,执行启动指令。
nginx容器的话,第一次发布比较麻烦,但是后面使用启动很快,一键搞定。
就拿前端项目的启动而言,这两种方式各有优劣。
对于我来说,因为我不去修改前端项目,我更喜欢使用nginx容器一些。我把我后端的端口改成了8081,nginx用8080,最终容器列表如下。
4.总结
- 对于开发,尤其是仅本地开发而言,容器化部署对于开发环境的运作效率没有提升,纯粹就是出于个人管理电脑环境的需求。
- 之所以用docker来部署前端,是因为我主要搞后端的,前端基本不会动。
- 调试用哪个IP地址才能把项目跑起来的过程中,我用host.docker.internal失败过,建议直接使用ifconfig查到本机IP来配置。
- 用好docker的话,搭建环境会更简单,不用到处找官网(某度找某官网得带双什么样的眼睛大家都懂),不需要软件的安装过程,软件版本的切换更是方便(尤其是像node这种需要低一些版本的)。总之我现在用过之后,觉得真香。
- 过程中我尝试过用宿主机去访问容器,安装了brew,下载了docker- connector,绕了一大圈,结果没用。docker for mac 的本质是虚拟机,Mac和Docker是物理隔离的。
- 通过docker desktop可以快速进入容器并通过命令行直接输入指令,也可以很方便地启动、关闭、重启容器,网上资料很多。