准备一台服务器
本次使用虚拟机,虚拟机系统 Ubuntu20.04,内存 4G,4核。
确保虚拟机能连接互联网。
Ubuntu20.04 安装 Docker
添加 Docker 的官方 GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
添加仓库到 apt 中:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
安装最新版本的 Docker:
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
docker-compose-plugin 就是 Docker Compose,上面的命令就安装了 Docker Compose
配置 Docker 镜像仓库:
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://mirror.baidubce.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
# 使用 Docker info 命令查看配置的加速仓库
docker info
启动一个 Hello World 容器测试 Docker 是否安装成功:
sudo docker run hello-world
看到如下内容就是安装成功:
使用如下命令查看 Docker 的版本以及 Docker Compose 的版本:
docker version
docker compose version
配置 Docker 命令可以使用非 Root 用户使用:
# 创建名为 docker 的用户组
sudo groupadd docker
# 将当前用户添加到 docker 用户组中
sudo usermod -aG docker $USER
# 激活
newgrp docker
# 验证非 Root 用户是否能使用 Docker 命令
docker run hello-world
配置 Docker 开机自启:
sudo systemctl enable docker.service
sudo systemctl enable containerd.service
# 关闭 Docker 开机自启
sudo systemctl disable docker.service
sudo systemctl disable containerd.service
打 ruoyi 后端的镜像
做必要的配置修改
master:
url: jdbc:mysql://mysql-service:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&connectTimeout=1000&socketTimeout=30000&autoReconnect=true&failOverReadOnly=false
username: ruoyi
password: 123456
编写 Dockerfile 文件
# 使用官方的 OpenJDK 8 镜像作为基础镜像
FROM openjdk:8-jdk-alpine
# 创建存放上传文件的目录
RUN mkdir -p /opt/project/ruoyi/ruoyi-backend/upload-file-path
# 创建存放日志的目录
RUN mkdir -p /opt/project/ruoyi/ruoyi-backend/logs
# 安装字体文件
RUN mkdir -p /etc/apk/
RUN touch /etc/apk/repositories
RUN echo -e 'https://mirrors.aliyun.com/alpine/v3.6/main/\nhttps://mirrors.aliyun.com/alpine/v3.6/community/' > /etc/apk/repositories
RUN apk --no-cache add ttf-dejavu fontconfig
RUN set -xe && apk --no-cache add ttf-dejavu fontconfig
# 设置工作目录
WORKDIR /opt/project/ruoyi/ruoyi-backend
# 将构建好的 JAR 文件复制到容器中
COPY ./ruoyi-admin/target/ruoyi-admin.jar ruoyi-admin.jar
# 暴露应用程序端口
EXPOSE 8080
# 启动应用程序
CMD ["nohup","java","-jar","/opt/project/ruoyi/ruoyi-backend/ruoyi-admin.jar", ">", "/opt/project/ruoyi/ruoyi-backend/logs/nohup.log", "&"]
IDEA 连接虚拟机中的 Docker 并打镜像
首先,检查 IDEA 是否安装了 Docker 插件:
版本比较新的 IDEA 默认都安装了这个插件,如果没有安装,安装一下。
确保我们虚拟机上安装了 Docker 和 Docker-compose,并启动了 Docker。
找到 IDEA 下方的 Services tab 栏,按照如下操作新建一个 Docker 连接:
配置连接 Docker 的 SSH 连接:
如果按照下面通过 password 认证连接 docker,idea 会显示 “Only key-pair ssh auth type is supported for docker connectons”:
所以需要改为 key-pair 的认证方式:
点击 OK。
移除路径映射后点击 OK:
右键我们创建的 Docker 连接,点击 “connection”,就连接成功了:
找到我们的 Dockerfile,右键这个文件,点击"Modify Run Configuration":
这里我们只需要修改 Image tag,也就是镜像名称,再点击 OK:
在打镜像前将我们的项目打包:
在 Dockerfile 文件的头部左侧的箭头位置点击 “Build image for dockerfile”,就会自动按照 Dockerfile 为我们打镜像:
打镜像完成后,就可以看到我们打的镜像了:
打 ruoyi 前端的镜像
编写 Dockerfile
在项目的根目录下创建 nginx.conf 配置文件,这个配置文件会拷贝到 ruoyi 前端镜像中,作为 Nginx 的配置文件:
user root;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name 127.0.0.1;
charset utf-8;
location / {
root /opt/project/ruoyi/ruoyi-front-code;
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://ruoyi-backend-service:8080/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
# 使用 Nginx 作为基础镜像
FROM nginx:1.12.2
# 将 nginx.conf 拷贝到容器中
COPY nginx.conf /etc/nginx/nginx.conf
# 创建存放前端编译后代码的目录
RUN mkdir -p /opt/project/ruoyi/ruoyi-front-code
# 将构建好的应用拷贝到 Nginx 的默认 web 目录
COPY dist /opt/project/ruoyi/ruoyi-front-code
# Expose 端口
EXPOSE 80
# 启动 Nginx 服务器
CMD ["nginx", "-g", "daemon off;"]
WebStorm 连接虚拟机中的 Docker 打镜像
连接和打包步骤和 IDEA 连接虚拟机 Docker 的步骤一模一样。
准备 docker compose 启动的所有容器需要的文件以及文件夹
准备 Redis 容器需要的配置文件
找到本次部署的 Redis 7.2.0 的 redis.conf 文件,编辑其中的以下内容:
# 修改连接 redis 的密码为 123456
requirepass 123456
# 注释掉 bind 127.0.0.1,bind 用于限制访问 Redis 的机器 ip,直接关掉
# bind 127.0.0.1
准备 MySQL 容器需要的初始化 SQL 文件
初始化的 SQL 文件在 Ruoyi 后端项目的如下位置:
容器首次启动的时候,我们需要执行这些初始化文件来建表、导入数据。
我们还需要一个创建数据库并创建一个用户的 sql 文件(create-database-user.sql):
-- 创建数据库
CREATE DATABASE `ry-vue` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 创建普通用户
grant all PRIVILEGES on ry-vue.* to ruoyi@'%' identified by '123456';
flush privileges;
use ry-vue;
我们编写一个初始化的 sql 文件,在这个 sql 文件中再调用 create-database-user.sql、quartz.sql 和 ry_20231130.sql 这三个文件:
source /opt/mysql-service/create-database-user.sql;
use ry-vue;
source /opt/mysql-service/quartz.sql;
source /opt/mysql-service/ry_20231130.sql;
注意:这里的路径都是容器中的路径,就是容器首次启动时会调用这些命令
在宿主机中创建文件夹以及上传需要文件
/opt/project 目录的目录结构如下:
project/
└── ruoyi
├── docker-compose.yml -- 下面我们要编写的 docker-compose 文件
├── mysql-service -- 存放 mysql-service 相关文件的目录
│ ├── other
│ │ ├── children-sql -- 子 sql 文件存放目录
│ │ │ ├── create-database-user.sql
│ │ │ ├── quartz.sql
│ │ │ └── ry_20231130.sql
│ │ └── init -- mysql 初始化执行目录,会挂载到 docker-entrypoint-initdb.d
│ │ └── arrange-sql.sql -- sql 编排文件,会依次调用 children-sql 文件夹中的 sql 文件
│ └── volumes -- mysql-service 挂载的容器卷
│ └── mysql
└── redis-service
└── volumes -- redis-service 挂载的容器卷
└── redis.conf
准备 SQL 文件
quartz.sql 和 ry_20231130.sql 在 ruoyi 后端代码根目录的 sql 文件夹中就有,直接拷贝即可,但是,需要在这两个 SQL 文件的第一行添加一行使用指定数据库的 SQL 语句:
use ry-vue;
create-database-user.sql 负责创建用户、数据库和为用户赋权:
-- 创建数据库
CREATE DATABASE `ry-vue` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 应先创建新用户
create user 'ruoyi'@'%' identified by '123456';
-- 执行授权
GRANT ALL PRIVILEGES ON `ry-vue`.* TO 'ruoyi'@'%';
-- 刷新
flush privileges;
-- 授权远程
ALTER USER 'ruoyi'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
-- 刷新
flush privileges;
arrange-sql.sql 负责调用其他 SQL 文件,起到对 SQL 文件进行编排的作用:
source /opt/mysql-service/create-database-user.sql;
SET NAMES utf8;
source /opt/mysql-service/quartz.sql;
source /opt/mysql-service/ry_20231130.sql;
编写 docker-compose.yml 文件
version: '3.8'
services:
mysql-service:
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD: 123456
ports:
- "3306:3306"
volumes:
- /opt/project/ruoyi/mysql-service/volumes/mysql:/var/lib/mysql
- /opt/project/ruoyi/mysql-service/other/init:/docker-entrypoint-initdb.d/
- /opt/project/ruoyi/mysql-service/other/children-sql:/opt/mysql-service/
redis-service:
image: redis:7.2.0-alpine3.18
restart: always
ports:
- "6379:6379"
volumes:
- /opt/project/ruoyi/redis-service/volumes/redis.conf:/usr/local/etc/redis/redis.conf
command: [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
ruoyi-backend-service:
image: ruoyi-backend:1.0
networks:
- ruoyi-network
ports:
- "8080:8080"
depends_on:
- mysql-service
- redis-service
ruoyi-frontend-service:
image: ruoyi-frontend:1.0
networks:
- ruoyi-network
ports:
- "80:80"
depends_on:
- ruoyi-backend-service
networks:
ruoyi-network:
driver: bridge
使用如下命令启动 docker-compose 文件中的容器:
docker compose up -d
遇到的问题
后端连接到 MySQL 错误
13:55:01.882 [main] ERROR c.a.d.p.DruidDataSource - [init,942] - init datasource error, url: jdbc:mysql://mysql-service:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT&connectTimeout=1000&socketTimeout=30000
com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:175)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64)
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:825)
at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:446)
at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:239)
at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:188)
at com.alibaba.druid.filter.FilterChainImpl.connection_connect(FilterChainImpl.java:118)
at com.alibaba.druid.filter.stat.StatFilter.connection_connect(StatFilter.java:232)
at com.alibaba.druid.filter.FilterChainImpl.connection_connect(FilterChainImpl.java:112)
at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1706)
at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1801)
at com.alibaba.druid.pool.DruidDataSource.init(DruidDataSource.java:938)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1462)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1458)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:83)
at org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource.getConnection(AbstractRoutingDataSource.java:194)
at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:159)
at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:117)
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:80)
at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:67)
at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:345)
at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:89)
at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:64)
at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:333)
at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:158)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:110)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:90)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:154)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:142)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:425)
at com.sun.proxy.$Proxy100.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:224)
at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:147)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:80)
at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:142)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86)
at com.sun.proxy.$Proxy101.selectConfigList(Unknown Source)
at com.ruoyi.system.service.impl.SysConfigServiceImpl.loadingConfigCache(SysConfigServiceImpl.java:177)
at com.ruoyi.system.service.impl.SysConfigServiceImpl.init(SysConfigServiceImpl.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:389)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:333)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:157)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:440)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1391)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:780)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:453)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:343)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1370)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1359)
at com.ruoyi.RuoYiApplication.main(RuoYiApplication.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure
修改 application-durid.yml 中的如下配置:
url: jdbc:mysql://mysql-service:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT&connectTimeout=1000&socketTimeout=30000&autoReconnect=true&failOverReadOnly=false
testOnBorrow: true
开启代理软件会导致访问虚拟机前端页面出现问题
访问后端接口报错
后端的报错日志:
15:38:29.831 [http-nio-8080-exec-8] ERROR c.r.f.w.e.GlobalExceptionHandler - [handleRuntimeException,93] - 请求地址'/captchaImage',发生未知异常.
java.lang.NullPointerException: null
at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264)
at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:219)
at sun.awt.FontConfiguration.init(FontConfiguration.java:107)
at sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:774)
at sun.font.SunFontManager$2.run(SunFontManager.java:431)
at java.security.AccessController.doPrivileged(Native Method)
at sun.font.SunFontManager.<init>(SunFontManager.java:376)
at sun.awt.FcFontManager.<init>(FcFontManager.java:35)
at sun.awt.X11FontManager.<init>(X11FontManager.java:57)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:83)
at java.security.AccessController.doPrivileged(Native Method)
at sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:74)
at java.awt.Font.getFont2D(Font.java:491)
at java.awt.Font.access$000(Font.java:224)
at java.awt.Font$FontAccessImpl.getFont2D(Font.java:228)
at sun.font.FontUtilities.getFont2D(FontUtilities.java:180)
at sun.font.StandardGlyphVector.initFontData(StandardGlyphVector.java:1126)
at sun.font.StandardGlyphVector.init(StandardGlyphVector.java:1115)
at sun.font.StandardGlyphVector.<init>(StandardGlyphVector.java:167)
at java.awt.Font.createGlyphVector(Font.java:2545)
at com.google.code.kaptcha.text.impl.DefaultWordRenderer.renderWord(DefaultWordRenderer.java:67)
at com.google.code.kaptcha.impl.DefaultKaptcha.createImage(DefaultKaptcha.java:43)
at com.ruoyi.web.controller.common.CaptchaController.getCode(CaptchaController.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:529)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:111)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:111)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:114)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at com.ruoyi.common.filter.RepeatableFilter.doFilter(RepeatableFilter.java:39)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:121)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter.doFilterInternal(JwtAuthenticationTokenFilter.java:42)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
http://doc.ruoyi.vip/ruoyi/other/faq.html#linux%E7%B3%BB%E7%BB%9F%E9%AA%8C%E8%AF%81%E7%A0%81%E4%B9%B1%E7%A0%81%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95