前言
- 最近又想直接把之前的基于ruoyi的前后端分离项目全部部署到树莓派上来,但是因为是刚接触docker和树莓派5的小白,甚至对linux操作都不太熟,导致踩了许多许多坑。原本这个项目是在windows上开发的,在windows我甚至都没部署过,就直接想部署到树莓派上。好在倒腾了两天,终于完成了😭。
- 整个过程对我帮助最大的三个链接,基本都是跟着链接教程走,但是很多步骤还是会发生报错,毕竟这里我基于的是树莓派5,后面会在介绍的时候详细说明。
Docker镜像选取
- 因为是树莓派arm64,所以拉取镜像的时候要格外注意。以下是我踩了很多不能用的镜像坑之后摸索出来的,现在你仍可在docker
hub
(机子可科学网上冲浪条件下)上直接拉取的镜像。
REPOSITORY TAG IMAGE ID CREATED SIZE SHARED SIZE UNIQUE SIZE CONTAINERS
ruoyi-admin latest 268e2764950f 25 hours ago 602MB 520.2MB 82.17MB 1
nginx latest 443d199e8bfc 3 weeks ago 193MB 97.11MB 95.89MB 1
redis latest db35fcf47f4b 7 weeks ago 139MB 97.11MB 41.99MB 1
openjdk 8 6fbf41d7a679 23 months ago 520MB 520.2MB 0B 1
ibex/debian-mysql-server-5.7 latest a53b3adb3cdc 5 years ago 230MB 0B 229.9MB 1
arm64v8/maven 3.5-jdk-8 bad818985d47 5 years ago 590MB 0B 590.4MB 1
- 如你所见,我们用到的镜像主要包括以上几个,其中第一个ruoyi-admin是使用
docker build
基于Dockerfile构建的,它被用来构部署ruoyi后端服务器。(具体操作将在后端搭章节详细讲解)- 至于nginx、redis、openjdk、debian-mysql-server-5.7、arm64/maven则可直接从docker
hub
上pull下来,使用如下命令:
docker pull nginx docker pull redis docker pull openjdk:8 docker pull ibex/debian-mysql-server-5.7 docker pull arm64v8/maven:3.5-jdk-8
- 至于nginx、redis、openjdk、debian-mysql-server-5.7、arm64/maven则可直接从docker
后端部署
- 在进行后端部署时,涉及到了mysql、redis、后端(ruoyi-admin)服务器的构建。他们分属于不同的docker容器,所以为了达到部署的目的,容器运行时肯定是要在同一网络下,参考开头提到的链接1的自建网络 --network net-ry。在对容器们进行
docker run
指令时注意添加这么一条--network net-ry
就可以保证他们在同一网络下。 - 整体介绍:首先讲后端部署。这一篇章主要介绍redis,mysql的分别部署,将ruoyi后端的
application.ym
文件的环境从原先的local
改为prod
,因为我们现在要部署而不是开发了。(这个主要看自己使用的ruoyi项目的具体情况,我的文件路径是这样的:~/EdgeCloudBackend/src/main/resources/application.ym
) - 使用Docker查看运行状态下的容器的ip地址和端口的命令:
docker ps
或者使用docker ps -a
查看全部容器 - 使用Docker查看运行状态下的容器的ip地址和端口的命令:
docker inspect 容器ID
# application.ym的部分代码
# Spring配置
spring:
#切换环境
profiles:
active: prod #就是这里从local改为prod
- 之后,继续更改同级的
application-prod.yml
文件,它用来指定你所部署的mysql、redis服务器的具体地址。只用更改redis的host、mysql后的主数据库源的url即可,我更改完了之后完整文件如下:(也可以先看后面章节,等mysql、redis容器部署完了确定了ip再回来改)
# 项目相关配置
ruoyi:
# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
profile: /home/ruoyi/laboratory/uploadPath
# 开发环境配置
server:
# 服务器的HTTP端口,默认为8080
port: 8080
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# tomcat最大线程数,默认为200
max-threads: 800
# Tomcat启动初始化的线程数,默认值25
min-spare-threads: 30
# Spring配置
spring:
redis:
# 地址
host: 172.68.0.2 #更改这里,这里就是在docker自建网络下运行你自己的redis的容器的ip地址
# 端口,默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://172.68.0.3:3306/laboratory?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
#更改这里,改为你自己的在docker自建网络下运行的mysql容器的ip地址。
username: root
password: 114514 #设置为你自己使用的数据库密码
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username:
login-password:
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
#授权相关地址
secret:
ip: 127.0.0.1
port: 7789
用Docker部署redis服务器
- 部署redis服务器时最简单的,参考链接1步骤即可。仅需在宿主机先创建好
redis.conf
,然后在docker run
的时候将宿主机的conf文件挂载到容器里即可。另外记得启动的时候要--network net-ry
用Docker部署mysql服务器
- 这一步非常重要,直接关系到后续ruoyi后端服务器运行jar包时能否成功启动的问题。同样可以参考链接1。由于之前已经说明过docker拉取mysql了,下面直接运行。
- 首先介绍我的文件夹布局:
kochiya@raspberrypi:~/Devbuild/ruoyiDocker/mysql $ tree . ├── conf │ └── my.cnf └── data └── xxx.sql
-
其中my.cnf文件中的内容如下:注意
lower_case_table_names=1
一定要加!如果你是使用的是树莓派或者同是arm64架构就一定要加!不然如果你数据库中表格名有大写字母的话会报错!本人踩坑亲测,, -
[client] default-character-set=utf8 [mysql] default-character-set=utf8 [mysqld] ##官方的配置 # # Remove leading # and set to the amount of RAM for the most important data # cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%. # innodb_buffer_pool_size = 128M # # Remove leading # to turn on a very important data integrity option: logging # changes to the binary log between backups. # log_bin # # Remove leading # to set options mainly useful for reporting servers. # The server defaults are faster for transactions and fast SELECTs. # Adjust sizes as needed, experiment to find the optimal values. # join_buffer_size = 128M # sort_buffer_size = 2M # read_rnd_buffer_size = 2M skip-host-cache skip-name-resolve datadir=/mysql lower_case_table_names=1 #记得一定要添加这一条!!忽略大小写 character-set-server=utf8 collation-server=utf8_unicode_ci explicit_defaults_for_timestamp=true
-
将mysql容器运行起来:
docker run -d --name mysql01 --network net-ry -v /home/kochiya/Devbuild/ruoyiDocker/mysql/conf/my.cnf:/etc/my.cnf -v /home/kochiya/Devbuild/ruoyiDocker/mysql/data:/var/lib/mysql --privileged=true --restart=always -e MYSQL_ROOT_PASSWORD=114514 -p 3306:3306 ibex/debian-mysql-server-5.7
- 指令简介:-d后台运行;-v将宿主机文件目录或文件挂载到“:”后的容器内部目录或文件,–p指定容器运行的端口;ibex/debian-mysql-server-5.7之前下载的容器镜像。
-
可以进入容器查看是否挂载成功:
docker exec -it mysql01 /bin/bash
执行后可以到处看看,看看指定的目录是否有挂载上宿主机的文件(conf文件和sql文件)。运行mysql -uroot -p进入mysql命令。使用show databases;
查看当前容器中的数据库。 -
mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.01 sec)
-
现在我们可以建立一个专门的数据库来存放我们的
xxx.sql
。指令如下:(比如我这里建立的数据库叫‘laboraroty’)带数据库插入完即可。同时这个数据库名也要与上面提到过的application-prod.yml
里的主数据库url的ip地址后的数据库名相对应。这样就搭建好了。 -
mysql> create database laboratory; mysql> use laboratory; mysql> source /var/lib/mysql/xxx.sql;
构建并部署ruoyi后端服务器容器
- 这里涉及到使用maven容器作为工具将后端项目打包。也是在选取和使用镜像方面踩了很多坑,都是关于jdk版本和maven版本以及ruoyi项目的xml文件中版本对应的问题,我使用的项目比较老,所以采用了maven3.5以及jdk1.8。如果你的项目也是要部署于arm64,强烈建议拉取我上面使用的
arm64v8/maven:3.5-jdk-8
也是废了些功夫试了很多错,终于找到了这个镜像。下面将其运行,将后端的目录挂载到容器中,我这里容器里是/mymaven
,我的宿主机后端目录结构如下:
kochiya@raspberrypi:~/Devbuild/EdgeCloudBackend $ ls
bin doc LICENSE pom.xml README.md ruoyi.iml ry.sh sql src target
其中这个target是在执行了mvn package
之后产生的。运行启动容器的指令:
docker run -v /home/kochiya/Devbuild/EdgeCloudBackend:/mymaven -w /mymaven arm64v8/maven:3.5-jdk-8 mvn package
运行完之后就可以看到你自己宿主机的项目目录多出来了target文件。
- 将target文件里的jar包复制到
ruoyi-admin
文件夹中,这个文件夹主要被建立来用于我们构建后端服务器容器的镜像。所以说这个文件夹里需要有项目后端的jar包和一个Dockerfile
文件,文件如下:这里我们将openjdk:8的镜像加入了我们的后端镜像,镜像套镜像的方法也是查找了好多文档,踩了很多坑试来的,之前尝试各种方法,比如直接在Dockerfile里下载jdk,但是疯狂报错
FROM openjdk:8
ENV APP_PATH=/root/app
WORKDIR $APP_PATH
ADD ./ruoyi.jar /$APP_PATH/apps.jar #添加你当前的ruoyi-admin文件夹中的jar文件进入虚拟容器
EXPOSE 8080 #露出8080端口,方便后续被前端部署nginx容器访问
ENTRYPOINT ["java","-jar"]
CMD ["apps.jar"] #执行jar文件,将后端运行起来
- 之后在
ruoyi-admin
文件夹下把docker build
起来:命令:docker build -t ruoyi-admin .
这里我们把构建的镜像也命名为ruoyi-admin
然后可以运行docker images
查看到底有没有被成功build出来。 - 之后我们运行这个镜像:
docker run -itd --name ruoyi-admin --network net-ry -p 8080:8080 ruoyi-admin
- 注意我们更改使用了-itd来运行,由于有-d的存在,容器是在后台执行的,我们在命令行运行完之后是不知道它内部究竟是运行好了还是报错了,这时候我们使用指令
docker logs ruoyi-admin
来查看内部到底运行打印出的内容是什么样的。如果顺利的话打印情况如下:我们顺利启动了ruoyi后端,且成功连接了mysql和redis
09:36:33.532 [main] INFO o.q.i.StdSchedulerFactory - [instantiate,1374] - Quartz scheduler 'RuoyiScheduler' initialized from an externally provided properties instance.
09:36:33.533 [main] INFO o.q.i.StdSchedulerFactory - [instantiate,1378] - Quartz scheduler version: 2.3.2
09:36:33.536 [main] INFO o.q.c.QuartzScheduler - [setJobFactory,2293] - JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@2f7a7219
09:36:33.658 [main] DEBUG c.r.p.m.m.S.selectJobAll - [debug,137] - ==> Preparing: select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark from sys_job
09:36:33.661 [main] DEBUG c.r.p.m.m.S.selectJobAll - [debug,137] - ==> Parameters:
09:36:33.666 [main] DEBUG c.r.p.m.m.S.selectJobAll - [debug,137] - <== Total: 3
09:36:35.231 [main] INFO s.d.s.w.PropertySourcedRequestMappingHandlerMapping - [initHandlerMethods,69] - Mapped URL path [/v2/api-docs] onto method [springfox.documentation.swagger2.web.Swagger2Controller#getDocumentation(String, HttpServletRequest)]
09:36:37.105 [main] INFO s.d.s.w.p.DocumentationPluginsBootstrapper - [start,160] - Context refreshed
09:36:37.158 [main] INFO s.d.s.w.p.DocumentationPluginsBootstrapper - [start,163] - Found 1 custom documentation plugin(s)
09:36:37.285 [main] INFO s.d.s.w.s.ApiListingReferenceScanner - [scan,41] - Scanning for api listing references
09:36:37.652 [main] INFO o.a.c.h.Http11NioProtocol - [log,173] - Starting ProtocolHandler ["http-nio-8080"]
09:36:37.696 [main] INFO c.r.RuoYiApplication - [logStarted,61] - Started RuoYiApplication in 15.989 seconds (JVM running for 16.605)
(♥◠‿◠)ノ゙ 若依启动成功 ლ(´ڡ`ლ)゙
.-------. ____ __
| _ _ \ \ \ / /
| ( ' ) | \ _. / '
|(_ o _) / _( )_ .'
| (_,_).' __ ___(_ o _)'
| |\ \ | || |(_,_)'
| | \ `' /| `-' /
| | \ / \ /
''-' `'-' `-..-'
前端部署
- 本以为后端搞好了,前端就简简单单用nginx容器部署了,但也是过程一波三折,因为疯狂出现403 forbidden报错,后面细说。
- 在前端工程中运行
npm run build
,会在前端文件夹目录中生成dist文件。为了便于讲解我们将dist文件夹复制到ruoyi-front文件夹中的html下,我的文件夹布局如下:
kochiya@raspberrypi:~/Devbuild/ruoyiDocker/ruoyi-front $ tree
.
├── conf
│ └── nginx.conf
├── html
│ └── dist
│ ├── favicon.ico
│ ├── index.html
│ ├── robots.txt
│ └── static
- 可以看到在这个文件夹中除了dist文件夹还有conf,这主要是用来配置nginx的配置文件,内容如下:
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
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;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80;#80端口
server_name localhost;
location / {
root /usr/share/nginx/html;#虚拟容器中的位置,dist下的内容要挂载到这里
index index.html;
}
location /prod-api/ {
proxy_pass http://172.68.0.5:8080/; #之前运行的ruoyi后端服务器容器的地址+对应端口,一定要设置
}
}
}
- 使用命令运行nginx容器:
docker run -itd --name nginx01 --network net-ry -p 80:80 -v /home/kochiya/Devbuild/ruoyiDocker/ruoyi-front/conf/nginx.conf:/etc/nginx/nginx.conf -v /home/kochiya/Devbuild/ruoyiDocker/ruoyi-front/html/dist:/usr/share/nginx/html --privileged=true --restart=always nginx
注意我这里是将dist文件夹内的内容挂载到了容器中,而非把dist文件夹挂载过去。我查到的很多教程有些是把dist整个挂载过去,如:-v ~/ruoyi-front/html/:/usr/share/nginx/html
但是我这要操作之后浏览器访问树莓派本机的80端口会产生如下403报错:(该报错使用docker logs nginx01
查看内部报错)
2024/07/12 12:03:25 [error] 29#29: *1 directory index of "/nginx/html/" is forbidden, client: 192.168.1.xxx server: localhost, request: "GET / HTTP/1.1", host: "192.168.1.xxx"
192.168.1.xxx - - [12/Jul/2024:12:03:25 +0000] "GET / HTTP/1.1" 403 153 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:127.0) Gecko/20100101 Firefox/127.0"
- 被这个问题困扰了半天。后面发现挂载dist内的内容到容器中就行了,这时候用你的宿主机(树莓派)的浏览器再访问
localhost:80
或你的树莓派ip:80
就能成功进入了,附成功的截图。
存在的问题
- 每次重启机器或者重启docker之后都可能会变docker运行的ip。即每个docker容器运行的ip不固定,一旦重启可能就要内部文件要要重新执行。
- 后续考虑使用dockercompose整合这些docker。不然感觉太麻烦了。