最近,学习项目模块容器化部署启动,遇到了网关模块(gate-way)中 无法加载验证码,的问题,过程些许复杂,记录,方便下次遇到同样问题快速解决。
CentOS7中安装配置docker与docker-compose 可以参考我这篇 基于CentOS7安装配置docker与docker-compose
一、错误信息与解决
1.1 错误信息
2023-08-23 00:52:16 [reactor-http-epoll-1] ERROR reactor.netty.http.server.HttpServer
2023-08-22T16:52:16.162476048Z - [a88e488c-1, L:/192.168.1.16:8080 - R:/192.168.1.12:4910]
2023-08-22T16:52:16.162478312Z java.lang.NoClassDefFoundError: Could not initialize class sun.font.SunFontManager
Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.UnsatisfiedLinkError: /usr/local/java/jdk-11.0.19/lib/libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory [in thread "reactor-http-epoll-2"]
2023-08-22T16:52:16.162718578Z at java.base/java.lang.ClassLoader$NativeLibrary.load0(Native Method)
主要错误:
java.lang.NoClassDefFoundError: Could not initialize class sun.font.SunFontManager -即无法初始化SunFontManager类和/usr/local/java/jdk-11.0.19/lib/libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory [in thread “reactor-http-epoll-2”], -即找不到这个的libfontmanager.so: libfreetype.so.6文件或者目录
总结:就是JDK里缺少了字体相关的包和文件,而验证码模块需要使用这些,所以导致无法生成并加载验证码。
项目模块容器化部署时使用的JDK镜像源是我自己使用dockerfile文件和docker build命令手动构建的,详细构建过程参考我这篇:使用dockerfile手动构建JDK11镜像运行容器并校验。
1.2 解决思路1-修改手动构建JDK的dockerfile(增加字体支持与中文支持)后重新手动构建JDK镜像源
解决思路1:
1、修改手动构建JDK的dockerfile文件,在dockerfile文件添加安装字体和支持中文的配置。
2、重新docker build构建(添加了安装字体和支持中文的配置)新的JDK镜像源。
3、在项目模块的dockerfile中配置新构建的JDK的源。
4、注意:如果你是在Windows或者mac的IDEA本地环境中修改了dockerfile,记得要将修改后的dockerfile同步到服务器中,否则对dockerfile文件所做的修改是不会生效的。
5、删除(配置旧JDK:没有添加安装字体和支持中文)容器化的项目模块镜像和容器。
//删除镜像
docker rmi 镜像标识 (镜像名或者ID)
//强制删除镜像
docker rmi -f 镜像标识 (镜像名或者ID)
//删除容器
docker rm 容器标识(容器名或ID)
6、重新将项目模块容器化(在项目模块的dockerfile中配置新构建的JDK的源后,重新构建镜像和启动容器(此处直接使用deploy.sh脚本启动,脚本里配置了docker-compose命令,结合docker-compose.yml文件一起使用,docker-compose命令可以同时多个构建项目模块镜像,并基于该镜像启动该项目容器))。
a、deploy.sh脚本启动,脚本里配置了docker-compose命令。
b、docker-compose命令结合docker-compose.yml文件一起使用。
c、docker-compose.yml文件中配置了docker-compose命令的build参数和项目模块dockerfile文件的位置。
d、docker-compose命令可以同时构建多个项目模块镜像,并基于该镜像启动该项目容器。
//deploy.sh脚本 文件部分内容
# 启动程序模块(必须)
services(){
docker-compose up -d ruoyi-gateway ruoyi-auth ruoyi-system
}
==============================================================================================================
//docker-compose.yml文件中部分内容
ruoyi-gateway:
image: ruoyi/ruoyi-gateway:1.7.0
container_name: ruoyi-gateway
build:
context: ./ruoyi2023731/gateway
#dockerfile的文件名称
dockerfile: dockerfile
environment:
# 时区上海
TZ: Asia/Shanghai
ports:
- "8080:8080"
volumes:
# 配置文件
- /docker/ruoyi-gateway/logs/:/ruoyi/gateway/logs
# skywalking 探针
- /docker/skywalking/agent/:/ruoyi/skywalking/agent
privileged: true
network_mode: "host"
ruoyi-auth:
image: ruoyi/ruoyi-auth:1.7.0
container_name: ruoyi-auth
build:
context: ./ruoyi2023731/auth
#dockerfile的文件名称
dockerfile: dockerfile
environment:
# 时区上海
TZ: Asia/Shanghai
ports:
- "9210:9210"
volumes:
# 配置文件
- /docker/ruoyi-auth/logs/:/ruoyi/auth/logs
# skywalking 探针
- /docker/skywalking/agent/:/ruoyi/skywalking/agent
privileged: true
network_mode: "host"
ruoyi-system:
# 镜像名 : 仓库/标签:版本
image: ruoyi/ruoyi-system:1.7.0
container_name: ruoyi-system
#djc构建镜像-images的命令 2023-8-3 18:11:13
# 指定dockerfile的上下文路径(相对当前docker-compose.yml的位置)
# 包含Dockerfile文件的目录路径,或者是git仓库的URL。
#当提供的值是相对路径时,它被解释为相对于当前compose文件的位置。
#该目录也是发送到Docker守护程序构建镜像的上下文。
build:
context: ./ruoyi2023731/system
#dockerfile的文件名称
dockerfile: dockerfile
#本来这里有个ports的,但显示与下面的ports冲突,可能只要配置一个就可以了,对位置没有太多要求
# depends_on: 依赖(以指定顺序启动)
# links也可以起到和depends_on相似的功能,即定义服务之间的依赖关系,从而确定服务启动的顺序
environment:
# 时区上海
TZ: Asia/Shanghai
ports:
- "9201:9201"
volumes:
# 配置文件
- /docker/ruoyi-system/logs/:/ruoyi/system/logs
# skywalking 探针
- /docker/skywalking/agent/:/ruoyi/skywalking/agent
privileged: true
network_mode: "host"
==============================================================================================================
缺点:如果自己使用dockerfile手动构建的JDK11镜像,添加了安装字体和支持中文的配置后,能解决现在的问题。不能保证后面这个
手动构建的JDK后面会不会发生其他问题。如果发生了其他错误,又要重新修改手动构建的JDK的dockerfile,删除手动构建的JDK的旧镜像和容器,重新执行脚本构建新的JDK,着实麻烦。
1.3 解决思路2-直接使用华为的JDK镜像源
二、修改手动构建JDK的dockerfile(增加字体支持与中文支持)后重新手动构建JDK镜像源
2.1 修改前的手动构建JDK的dockerfile
# 建立一个新的镜像文件,配置模板:新建立的镜像是以centos为基础模板
# 因为jdk必须运行在操作系统之上
#每一个指令都会在镜像上创建一个新的层,每一个指令的前缀都必须是大写的。
#第一条FROM,指定使用哪个镜像源
FROM centos:7.9.2009
#维护者 作者 邮箱
MAINTAINER djcking <djc**4*****@qq.com>
#RUN 指令告诉docker 在镜像内执行命令,安装了什么
#创建一个新目录来存储jdk文件
RUN mkdir "/usr/local/java"
#将jdk压缩文件复制到镜像中,它将自动解压缩tar文件
ADD jdk-11.0.19_linux-x64_bin.tar.gz /usr/local/java
# 设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 设置环境变量
ENV JAVA_HOME /usr/local/java/jdk-11.0.19
ENV PATH $JAVA_HOME/bin:$PATH
# VOLUME 指定了临时文件目录为/tmp
# 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp
VOLUME /tmp
2.2 修改后的手动构建JDK的dockerfile
增加了安装支持的字体 和 设置中文支持 的配置。
# 建立一个新的镜像文件,配置模板:新建立的镜像是以centos为基础模板
# 因为jdk必须运行在操作系统之上
#每一个指令都会在镜像上创建一个新的层,每一个指令的前缀都必须是大写的。
#第一条FROM,指定使用哪个镜像源
FROM centos:7.9.2009
#维护者 作者 邮箱
MAINTAINER djcking <djc**4*****@qq.com>
#RUN 指令告诉docker 在镜像内执行命令,安装了什么
#创建一个新目录来存储jdk文件
RUN mkdir "/usr/local/java"
#将jdk压缩文件复制到镜像中,它将自动解压缩tar文件
ADD jdk-11.0.19_linux-x64_bin.tar.gz /usr/local/java
# 设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 安装支持的字体 【修改处1】
RUN yum install fontconfig -y
# 设置中文支持 【修改处2】
# setup language 解决中文乱码
ENV LANG C.UTF-8
# 设置环境变量
ENV JAVA_HOME /usr/local/java/jdk-11.0.19
ENV PATH $JAVA_HOME/bin:$PATH
# VOLUME 指定了临时文件目录为/tmp
# 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp
VOLUME /tmp
1.3 上传修改后JDK的dockerfile和jdk压缩包文件
将dockerfile和jdk镜像文件上传到**/home/mydockerfile2**文件中
2.4 构建JDK镜像
注意最后有个点,代表使用当前路径的 Dockerfile 进行构建
docker build -t djcjdk202395:11 .
2.5 在gateway的dockerfile中配置自己的djcjdk202395-11
# 基础镜像
#FROM anapsix/alpine-java:8_server-jre_unlimited
#FROM openjdk:11-jre
#FROM anapsix/alpine-java:11_server-jre_unlimited
#配置自己的djcjdk202395:11 2023-9-5 00:36:42
#FROM djcjdk202395:11
# author
MAINTAINER djc
# 挂载目录
VOLUME /home/ruoyi202381
# 创建目录
RUN mkdir -p /home/ruoyi202381
# 指定路径
WORKDIR /home/ruoyi202381
# 复制jar文件到路径
COPY ./jar/gateway.jar /home/ruoyi202381/gateway.jar
# 启动用户服务 网关服务
ENTRYPOINT ["java","-jar","gateway.jar"]
2.6 删除旧的gateway镜像和容器(没有中文支持和字体支持)
删除镜像与容器可参考我这篇 Docker基本操作之删除容器Container和删除镜像IMAGE
//删除镜像
docker rmi 镜像标识 (镜像名或者ID)
//强制删除镜像
docker rmi -f 镜像标识 (镜像名或者ID)
//删除容器
docker rm 容器标识(容器名或ID)
2.7 重新构建gateway模块
cd /opt/docker-compose/ruoyicloudplus/docker/ruoyi2023731/gateway
docker-compose up -d ruoyi-gateway
构建失败
Exception in thread "main" java.lang.UnsupportedClassVersionError: com/ruoyi/gateway/RuoYiGatewayApplication has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
2023-09-05T00:50:49.629236904Z at java.lang.ClassLoader.defineClass1(Native Method)
说明JDK有问题,这个问题之前遇到过。
//数字52对应Java8,55对应Java11。
//2023-11-8 02:17:40
被JRE11编译,现在的JRE为JRE8。
三、直接使用华为的JDK镜像源
3.1 获取镜像源
//1-拉取华为JDK镜像源
docker pull swr.ap-southeast-3.myhuaweicloud.com/oracle/jdk:11
//2-给镜像源打标签
docker tag 457a2c8597a1 hw-oracle/jdk:11
3.2 删除三大模块旧的镜像与容器
gateway(网关)、auth(认证授权中心)、system(系统模块)
删除镜像与容器可参考我这篇 Docker基本操作之删除容器Container和删除镜像IMAGE
//删除镜像
docker rmi 镜像标识 (镜像名或者ID)
//强制删除镜像
docker rmi -f 镜像标识 (镜像名或者ID)
//删除容器
docker rm 容器标识(容器名或ID)
3.3 三大模块中配置使用华为的镜像源
3.3.1 gateway的dockerfile配置使用华为的JDK镜像源
# 基础镜像
#FROM anapsix/alpine-java:8_server-jre_unlimited
#FROM openjdk:11-jre
#FROM anapsix/alpine-java:11_server-jre_unlimited
#配置自己的djcjdk202395:11 2023-9-5 00:36:42
#FROM djcjdk202395:11
#配置使用华为的JDK镜像源 2023-9-5 01:12:48
FROM hw-oracle/jdk:11
# author
MAINTAINER djc djc****@qq.com
# 挂载目录
VOLUME /home/ruoyi202381
# 创建目录
RUN mkdir -p /home/ruoyi202381
# 指定路径
WORKDIR /home/ruoyi202381
# 复制jar文件到路径
COPY ./jar/gateway.jar /home/ruoyi202381/gateway.jar
# 启动用户服务 网关服务
ENTRYPOINT ["java","-jar","gateway.jar"]
3.3.2 auth的dockerfile配置使用华为的JDK镜像源
# 基础镜像
#FROM anapsix/alpine-java:8_server-jre_unlimited
#FROM openjdk:11-jre
#FROM anapsix/alpine-java:11_server-jre_unlimited
#配置使用华为的JDK镜像源 2023-9-5 01:12:48
FROM hw-oracle/jdk:11
# author
MAINTAINER djc <djc**4*****@qq.com>
# 挂载目录
VOLUME /home/ruoyi202381
# 创建目录
RUN mkdir -p /home/ruoyi202381
# 指定路径
WORKDIR /home/ruoyi202381
# 复制jar文件到路径
COPY ./jar/auth.jar /home/ruoyi202381/auth.jar
# 启动用户服务 认证授权中心服务
ENTRYPOINT ["java","-jar","auth.jar"]
3.3.3 system模块的dockerfile配置使用华为的JDK镜像源
# 基础镜像
#FROM anapsix/alpine-java:8_server-jre_unlimited
#FROM openjdk:11-jre
#FROM anapsix/alpine-java:11_server-jre_unlimited
#配置使用华为的JDK镜像源 2023-9-5 01:12:48
FROM hw-oracle/jdk:11
# author
MAINTAINER djc <djc**4*****@qq.com>
# 挂载目录
VOLUME /home/ruoyi202381
# 创建目录
RUN mkdir -p /home/ruoyi202381
# 指定路径
WORKDIR /home/ruoyi202381
# 复制jar文件到路径
COPY ./jar/system.jar /home/ruoyi202381/system.jar
# 启动用户服务 系统模块服务
ENTRYPOINT ["java","-jar","system.jar"]
3.4 重新构建三大模块镜像并启动三大模块容器
3.4.1 前端打dist包和安装配置nginx(nginx.conf)
解决404问题和部署项目后端接受不到请求
修改ruoyi-ui/.env.production中的 VUE_APP_BASE_API
ruoyi-ui/.env.production 中的 VUE_APP_BASE_API配置成:VUE_APP_BASE_API=‘http://192.168.1.16:8080’
ip要配置为公网ip,端口号与后端一致。
修改vue.config.js
target: http://localhost:8080修改成 target: http://192.168.1.16:8080
3.4.2 启动后端三大模块镜像(gateway-auth-system)
关键点:
1、注意防火墙设置开放几个服务端口。
设置防火墙开放ruoyigateway8080、ruoyi-system9201、ruoyi-auth9210、
mysql3306、redis6379、nacos8848、seata-server8091端口。
2、执行脚本命令sh deploy.sh base,启动基础环境中nacos可能会报No dataset source错误,这是因为nacos的后台配置文件存放在mysql中,而mysql还没有完全启动成功,nacos就已经启动了,导致无法加载配置文件。也就是说,必须mysql启动成功后,nacos才能加载到配置文件,才能完全启动成功。
3、启动后端基础环境和三大服务模块后,分别查看容器是否正常启动成功。(使用docker ps命令查看正在运行的容器是否包含四个后端基础环境和三大服务模块共七个镜像和七个容器。)
//1-进入登录脚本文件所在的文件夹
cd /opt/docker-compose/ruoyicloudplus/docker/sh2023731
//2-启动后端基础环境mysql redis nacos seata-server
//实际执行docker-compose up -d mysql redis nacos seata-server
sh deploy.sh base
//3-启动服务ruoyi-gateway ruoyi-auth ruoyi-system
//自动重新构建ruoyi-gateway和ruoyi-auth和ruoyi-system镜像和运行ruoyi-auth和ruoyi-system容器
//实际执行docker-compose up -d ruoyi-gateway ruoyi-auth ruoyi-system
sh deploy.sh services
sh2023731中的deploy.sh脚本内容
#!/bin/sh
# 使用说明,用来提示输入参数
usage(){
echo "Usage: sh 执行脚本.sh [base|services|stop|rm]"
exit 1
}
# 启动基础环境(必须)
base(){
docker-compose up -d mysql redis nacos seata-server
}
# 启动程序模块(必须)
services(){
docker-compose up -d ruoyi-gateway ruoyi-auth ruoyi-system
}
# 关闭所有环境/模块
stop(){
docker-compose stop
}
# 删除所有环境/模块
rm(){
docker-compose rm
}
# 根据输入参数,选择执行对应方法,不输入则执行使用说明
case "$1" in
"base")
base
;;
"services")
services
;;
"stop")
stop
;;
"rm")
rm
;;
*)
usage
;;
esac
3.4.3 重新构建三大模块镜像并启动三大模块容器过程截图
3.4.4 成功加载验证码和解决控制台乱码
注意:验证码没有出现,可能是 后端基础环境mysql redis nacos seata-server 某个模块容器没有启动成功。
登录成功
控制台ruoyi-system的Dubbo服务响应与调用
退出系统
3.4.5 关闭所有模块容器
关闭所有模块容器命令
【关闭所有模块】
//1-进入登录脚本文件所在的文件夹
cd /opt/docker-compose/ruoyicloudplus/docker/sh2023731
//2-启动关闭所有模块的脚本
//实际执行docker-compose stop
sh deploy.sh stop
#sh2023731中的deploy.sh脚本内容 相关内容
# 关闭所有环境/模块 2023-9-4 23:08:12
stop(){
docker-compose stop
}
关闭所有模块容器截图
关闭所有模块-sh deploy.sh stop-后台日志
3.5 通过sh deploy.sh services 脚本命令构建三大项目模块镜像的原理
//3-启动服务ruoyi-gateway ruoyi-auth ruoyi-system
//自动重新构建ruoyi-gateway和ruoyi-auth和ruoyi-system镜像和运行ruoyi-auth和ruoyi-system容器
//实际执行docker-compose up -d ruoyi-gateway ruoyi-auth ruoyi-system
sh deploy.sh services
实际执行的是docker-compose up -d ruoyi-gateway ruoyi-auth ruoyi-system这句docker-compose命令,docker-compose可以批量构建镜像和启动容器。
CentOS7中安装配置docker与docker-compose 可以参考我这篇 基于CentOS7安装配置docker与docker-compose
1、安装好docker与docker-compose的环境。
2、配置好JDK镜像源。
3、编写三大模块的docker-compose.yml文件。
4、三大模块的docker-compose.yml中build参数用来构建镜像、dockerfile参数用来指定dockerfile
文件的位置。
5、Java -jar jar包名称 命令直接在容器中运行模块。
前面三大模块dockerfile中最后一行中出现的gateway.jar、auth.jar、system.jar
实际是后端通过maven打包生成的jar。
关于Java -jar具体看3.5 jar文件和脚本文件的说明
原理画了一个图,方便自己理解。(以gateway网关服务模块为例,auth认证授权模块,system模块同理)
3.5 jar文件和脚本文件的说明
前面三大模块dockerfile中最后一行中出现的gateway.jar、auth.jar、system.jar实际是后端通过maven打包生成的jar。
3.5.1 刷新maven依赖&clean项目
3.5.2 package-打包
3.5.3 执行copy.sh
maven package打包生成的jar名称,默认是以模块名称作为jar包的名称。
如ruoyi-gateway模块打包默认生成的jar包的名称就是ruoyi-gateway.jar。
ruoyi-auth模块打包默认生成的jar包的名称就是ruoyi-auth.jar。
ruoyi-system模块打包默认生成的jar包的名称就是ruoyi-gateway.jar。
这里通过编写并运行copy.sh脚本的方式,将这三个jar文件复制到对应的文件夹,并改变简化其名称。
#!/bin/bash
# 创建目标目录
#db存放sql源文件
mkdir -p ../mysql2023731/db2023731
#ruoyi-gateway 网关服务 (网关服务(RuoYiGatewayApplication)启动8080)
mkdir -p ../ruoyi2023731/gateway/jar
#ruoyi-auth 认证授权中心 (认证授权中心RuoYiAuthApplication启动9210)
mkdir -p ../ruoyi2023731/auth/jar
#系统模块RuoYiSystemApplication启动9201
mkdir -p ../ruoyi2023731/system/jar
# 复制sql文件 到/docker/mysql/db2023731文件夹中
echo "begin copy sql "
cp ../../sql/ry-cloud.sql ../mysql2023731/db2023731
cp ../../sql/ry-config.sql ../mysql2023731/db2023731
cp ../../sql/ry-job.sql ../mysql2023731/db2023731
cp ../../sql/ry-seata.sql ../mysql2023731/db2023731
cp ../../sql/test.sql ../mysql2023731/db2023731
echo "end copy sql "
# 复制jar文件
echo "begin copy jar "
cp ../../ruoyi-gateway/target/ruoyi-gateway.jar ../ruoyi2023731/gateway/jar/gateway.jar
cp ../../ruoyi-auth/target/ruoyi-auth.jar ../ruoyi2023731/auth/jar/auth.jar
cp ../../ruoyi-modules/ruoyi-system/target/ruoyi-system.jar ../ruoyi2023731/system/jar/system.jar
echo "end copy jar "
run copy.sh
system.jar
auth.jar
gateway.jar
run copy.sh脚本命令分析
3.6 java -jar 启动用户服务的命令
在三大模块的dockerfile中最后一行分别都配置了,下面这行命令。
# 启动用户服务 网关服务
ENTRYPOINT ["java","-jar","gateway.jar"]
# 启动用户服务 认证授权中心服务
ENTRYPOINT ["java","-jar","auth.jar"]
# 启动用户服务 系统模块服务
ENTRYPOINT ["java","-jar","system.jar"]
这里实际是将SpringBoot的项目,直接以jar包的方式直接启动部署。
详细介绍请看官方文档Creating an Executable Jar
1、首先在项目的pom.xml文件中的dependencies外面,添加以下内容
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
此处以boot3-01-demo为例
2、mvn clean package 打包boot3-01-demo项目模块
//1-maven打包
//执行maven打包命令mvn clean package,或者在IDEA中使用图形化界面打包
//IDEA中使用图形化界面打包:选中lifecycle中的clean和package,随即点击上方的右三角Run Maven Build
mvn clean package
执行完maven打包命令mvn clean package后,会在target目录中会出现一个jar包。名为
项目名-0.0.1-SNAPSHOT.jar ,这里就是boot3-01-demo-1.0-SNAPSHOT.jar 。
//2-如果想进一步查看,就执行 jar tvf命令
//target是个目录
jar tvf target/boot3-01-demo-1.0-SNAPSHOT.jar
应该看到一个小得多的文件,名为boot3-01-demo-1.0-SNAPSHOT.jar.original在target目录。这是Maven在Spring Boot重新打包之前创建的原始jar文件。
//3-运行
语法:java -jar jar文件名
java -jar boot3-01-demo-1.0-SNAPSHOT.jar
//4-停止
按ctrl c
这里实际是将SpringBoot的项目,直接以jar包的方式直接启动部署。
详细介绍请看官方文档Creating an Executable Jar